您的位置:首页 > 其它

杂项: 线程的同步于互斥,生产者问题,读写者问题实现

2014-06-19 15:07 387 查看
虽然写过几个多线程程序,但感觉还是理解不透,迷迷糊糊,今天把基本概念理清了一把,谢谢参考资料中的博客,部分资料参考那个上面,csdn上牛人很多,想理解一些东西建议在csdn网站上面查找,有很多人的博客要比那些大学老师讲的还透彻,易理解。

下面概念和实例,在本机上已经亲自尝试。用多线程解决问题的思路,第一要准确理解同步和互斥的含义,找到问题中需要同步等待和需要互斥访问的地方。 第二要理解临界区,信号量,事件含义,用在恰当的场合。

1.线程(进程)之间的制约关系?

当线程并发执行时,由于资源共享和线程协作,使用线程之间会存在以下两种制约关系。

(1).间接相互制约。一个系统中的多个线程必然要共享某种系统资源,如共享CPU,共享I/O设备,所谓间接相互制约即源于这种资源共享,打印机就是最好的例子,线程A在使用打印机时,其它线程都要等待。

(2).直接相互制约。这种制约主要是因为线程之间的合作,如有线程A将计算结果提供给线程B作进一步处理,那么线程B在线程A将数据送达之前都将处于阻塞状态。

间接相互制约可以称为互斥,直接相互制约可以称为同步,对于互斥可以这样理解,线程A和线程B互斥访问某个资源则它们之间就会产个顺序问题——要么线程A等待线程B操作完毕,要么线程B等待线程操作完毕,这其实就是线程的同步了。因此同步包括互斥,互斥其实是一种特殊的同步。

 
2.临界资源和临界区

在一段时间内只允许一个线程访问的资源就称为临界资源或独占资源,计算机中大多数物理设备,进程中的共享变量等待都是临界资源,它们要求被互斥的访问。每个进程中访问临界资源的代码称为临界区

3. 线程同步实例1: 编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推。



#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include <iostream>
#include <string>
#include <stack>
#include <windows.h>
#include <process.h>
using namespace std;

const int THREAD_NUM = 10;
HANDLE            ga,gb,gc;

unsigned int __stdcall FunA(void *pPM)
{
Sleep(50);//some work should to do
printf("A\n");
ReleaseSemaphore(gb, 1, NULL);//递增信号量B的资源数

return 0;
}

unsigned int __stdcall FunB(void *pPM)
{
Sleep(50);//some work should to do
printf("B\n");
ReleaseSemaphore(gc, 1, NULL);//递增信号量C的资源数

return 0;
}

unsigned int __stdcall FunC(void *pPM)
{
Sleep(50);//some work should to do
printf("C\n");
ReleaseSemaphore(ga, 1, NULL);//递增信号量A的资源数

return 0;
}

int main()
{
//初始化信号量
ga = CreateSemaphore(NULL, 1, 1, NULL);//当前1个资源,最大允许1个同时访问
gb = CreateSemaphore(NULL, 0, 1, NULL);//当前0个资源,最大允许1个同时访问
gc = CreateSemaphore(NULL, 0, 1, NULL);//当前0个资源,最大允许1个同时访问

HANDLE  handle[THREAD_NUM];
int i = 0;
while (i < THREAD_NUM)
{
WaitForSingleObject(ga, INFINITE);  //等待信号量A>0
handle[i] = (HANDLE)_beginthreadex(NULL, 0, FunA, &i, 0, NULL);
WaitForSingleObject(gb, INFINITE);  //等待信号量B>0
handle[i] = (HANDLE)_beginthreadex(NULL, 0, FunB, &i, 0, NULL);
WaitForSingleObject(gc, INFINITE);  //等待信号量C>0
handle[i] = (HANDLE)_beginthreadex(NULL, 0, FunC, &i, 0, NULL);

++i;
}
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);

//销毁信号量
CloseHandle(ga);
CloseHandle(gb);
CloseHandle(gc);
for (i = 0; i < THREAD_NUM; i++)
CloseHandle(handle[i]);
return 0;
}

4. 生产者消费者问题: 有一个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个有多个缓冲区的缓冲池,生产者将它生产的产品放入一个缓冲区中,消费者可以从缓冲区中取走产品进行消费,所有生产者和消费者都是异步方式运行的,但它们必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个已经装满产品且尚未被取走的缓冲区中投放产品。


//1生产者 2消费者 4缓冲区
#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include <iostream>
#include <string>
#include <stack>
#include <windows.h>
#include <process.h>
using namespace std;

//设置控制台输出颜色
BOOL SetConsoleColor(WORD wAttributes)
{
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConsole == INVALID_HANDLE_VALUE)
return FALSE;

return SetConsoleTextAttribute(hConsole, wAttributes);
}

const int END_PRODUCE_NUMBER = 8;   //生产产品个数
const int BUFFER_SIZE = 4;          //缓冲区个数
int g_Buffer[BUFFER_SIZE];          //缓冲池
int g_i, g_j;
CRITICAL_SECTION g_cs;              //信号量与关键段
HANDLE g_hSemaphoreBufferEmpty, g_hSemaphoreBufferFull;

//生产者线程函数
unsigned int __stdcall ProducerThreadFun(PVOID pM)
{
for (int i = 1; i <= END_PRODUCE_NUMBER; i++)
{
//等待有空的缓冲区出现
WaitForSingleObject(g_hSemaphoreBufferEmpty, INFINITE);

//互斥的访问缓冲区
EnterCriticalSection(&g_cs);
g_Buffer[g_i] = i;
printf("生产者在缓冲池第%d个缓冲区中投放数据%d\n", g_i, g_Buffer[g_i]);
g_i = (g_i + 1) % BUFFER_SIZE;
LeaveCriticalSection(&g_cs);

//通知消费者有新数据了
ReleaseSemaphore(g_hSemaphoreBufferFull, 1, NULL);
}
printf("生产者完成任务,线程结束运行\n");
return 0;
}

//消费者线程函数
unsigned int __stdcall ConsumerThreadFun(PVOID pM)
{
while (true)
{
//等待非空的缓冲区出现
WaitForSingleObject(g_hSemaphoreBufferFull, INFINITE);

//互斥的访问缓冲区
EnterCriticalSection(&g_cs);
SetConsoleColor(FOREGROUND_GREEN);
printf("  编号为%d的消费者从缓冲池中第%d个缓冲区取出数据%d\n", GetCurrentThreadId(), g_j, g_Buffer[g_j]);
SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);

g_j = (g_j + 1) % BUFFER_SIZE;
LeaveCriticalSection(&g_cs);

Sleep(50); //some other work to do

ReleaseSemaphore(g_hSemaphoreBufferEmpty, 1, NULL);
}
SetConsoleColor(FOREGROUND_GREEN);
printf("  编号为%d的消费者收到通知,线程结束运行\n", GetCurrentThreadId());
SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
return 0;
}

int main()
{
InitializeCriticalSection(&g_cs);
//初始化信号量,一个记录有产品的缓冲区个数,另一个记录空缓冲区个数.
g_hSemaphoreBufferEmpty = CreateSemaphore(NULL, 4, 4, NULL);
g_hSemaphoreBufferFull  = CreateSemaphore(NULL, 0, 4, NULL);
g_i = 0;
g_j = 0;
memset(g_Buffer, 0, sizeof(g_Buffer));

const int THREADNUM = 3;
HANDLE hThread[THREADNUM];
//生产者线程
hThread[0] = (HANDLE)_beginthreadex(NULL, 0, ProducerThreadFun, NULL, 0, NULL);
//消费者线程
hThread[1] = (HANDLE)_beginthreadex(NULL, 0, ConsumerThreadFun, NULL, 0, NULL);
hThread[2] = (HANDLE)_beginthreadex(NULL, 0, ConsumerThreadFun, NULL, 0, NULL);
WaitForMultipleObjects(THREADNUM, hThread, TRUE, INFINITE);

for (int i = 0; i < THREADNUM; i++)
CloseHandle(hThread[i]);

//销毁信号量和关键段
CloseHandle(g_hSemaphoreBufferEmpty);
CloseHandle(g_hSemaphoreBufferFull);
DeleteCriticalSection(&g_cs);
return 0;
}


5:读者写者问题:这也是一个非常经典的多线程题目,题目大意如下:有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者读时写者也不能写。

#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include <iostream>
#include <string>
#include <stack>
#include <windows.h>
#include <process.h>
using namespace std;

//读者与写者问题
#include <stdio.h>
#include <process.h>
#include <windows.h>
//设置控制台输出颜色
BOOL SetConsoleColor(WORD wAttributes)
{
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConsole == INVALID_HANDLE_VALUE)
return FALSE;

return SetConsoleTextAttribute(hConsole, wAttributes);
}
const int READER_NUM = 5;  //读者个数
//关键段和事件
CRITICAL_SECTION g_cs, g_cs_writer_count;
HANDLE g_hEventWriter, g_hEventNoReader;
int g_nReaderCount;
//读者线程输出函数(变参函数的实现)
void ReaderPrintf(char *pszFormat, ...)
{
va_list   pArgList;

va_start(pArgList, pszFormat);
EnterCriticalSection(&g_cs);
vfprintf(stdout, pszFormat, pArgList);
LeaveCriticalSection(&g_cs);
va_end(pArgList);
}
//读者线程函数
unsigned int __stdcall ReaderThreadFun(PVOID pM)
{
ReaderPrintf("     编号为%d的读者进入等待中...\n", GetCurrentThreadId());
//等待写者完成
WaitForSingleObject(g_hEventWriter, INFINITE);

//读者个数增加
EnterCriticalSection(&g_cs_writer_count);
g_nReaderCount++;
if (g_nReaderCount == 1)
ResetEvent(g_hEventNoReader);
LeaveCriticalSection(&g_cs_writer_count);

//读取文件
ReaderPrintf("编号为%d的读者开始读取文件...\n", GetCurrentThreadId());

Sleep(rand() % 100);

//结束阅读,读者个数减小,空位增加
ReaderPrintf(" 编号为%d的读者结束读取文件\n", GetCurrentThreadId());

//读者个数减少
EnterCriticalSection(&g_cs_writer_count);
g_nReaderCount--;
if (g_nReaderCount == 0)
SetEvent(g_hEventNoReader);
LeaveCriticalSection(&g_cs_writer_count);

return 0;
}
//写者线程输出函数
void WriterPrintf(char *pszStr)
{
EnterCriticalSection(&g_cs);
SetConsoleColor(FOREGROUND_GREEN);
printf("     %s\n", pszStr);
SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
LeaveCriticalSection(&g_cs);
}
//写者线程函数
unsigned int __stdcall WriterThreadFun(PVOID pM)
{
WriterPrintf("写者线程进入等待中...");
//等待读文件的读者为零
WaitForSingleObject(g_hEventNoReader, INFINITE);
//标记写者正在写文件
ResetEvent(g_hEventWriter);

//写文件
WriterPrintf("  写者开始写文件.....");
Sleep(rand() % 100);
WriterPrintf("  写者结束写文件");

//标记写者结束写文件
SetEvent(g_hEventWriter);
return 0;
}

int main()
{
printf("  读者写者问题\n");
printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");

//初始化事件和信号量
InitializeCriticalSection(&g_cs);
InitializeCriticalSection(&g_cs_writer_count);

//手动置位,初始已触发
g_hEventWriter = CreateEvent(NULL, TRUE, TRUE, NULL);
g_hEventNoReader  = CreateEvent(NULL, FALSE, TRUE, NULL);
g_nReaderCount = 0;

int i;
HANDLE hThread[READER_NUM + 1];
//先启动二个读者线程
for (i = 1; i <= 2; i++)
hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);
//启动写者线程
hThread[0] = (HANDLE)_beginthreadex(NULL, 0, WriterThreadFun, NULL, 0, NULL);
Sleep(50);
//最后启动其它读者结程
for ( ; i <= READER_NUM; i++)
hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);
WaitForMultipleObjects(READER_NUM + 1, hThread, TRUE, INFINITE);
for (i = 0; i < READER_NUM + 1; i++)
CloseHandle(hThread[i]);

//销毁事件和信号量
CloseHandle(g_hEventWriter);
CloseHandle(g_hEventNoReader);
DeleteCriticalSection(&g_cs);
DeleteCriticalSection(&g_cs_writer_count);
return 0;
}


引用,请转自出处。

参考资料: http://blog.csdn.net/dazhong159/article/details/7948327#

参考资料:http://blog.csdn.net/morewindows/article/details/7538247

参考资料:http://blog.csdn.net/u010236550/article/details/12372319
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐