多线程编程示例3(生产者消费者)
生产者消费者问题是一个著名的线程同步问题,该问题描述如下:有一个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个具有多个缓冲区的缓冲池,生产者将它生产的产品放入一个缓冲区中,消费者可以从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个已经放入产品的缓冲区中再次投放产品。
这个生产者消费者题目不仅常用于操作系统的课程设计,也常常在程序员和软件设计师考试中出现。并且在计算机考研的专业课考试中也是一个非常热门的问题。因此现在就针对这个问题进行详细深入的解答。
首先来简化问题,先假设生产者和消费者都只有一个,且缓冲区也只有一个。这样情况就简便多了。
第一.从缓冲区取出产品和向缓冲区投放产品必须是互斥进行的。可以用关键段和互斥量来完成。
第二.生产者要等待缓冲区为空,这样才可以投放产品,消费者要等待缓冲区不为空,这样才可以取出产品进行消费。并且由于有二个等待过程,所以要用二个事件或信号量来控制。
以上这段分析见http://blog.csdn.net/morewindows/article/details/7577591,不能再赞了。
以下为自己实现的代码:(多生产者未实现,不过可以参照多消费者,将生产者的数量放在临界区进行操作)
#pragma once #define _CRTDBG_MAP_ALLOC #include<cstdio> #include<Windows.h> #include<crtdbg.h> #include<process.h> //设置控制台输出颜色 BOOL SetConsolecolor(WORD wAttributes) { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); if (hConsole==INVALID_HANDLE_VALUE) { return false; } return SetConsoleTextAttribute(hConsole, wAttributes); } //1生产者 1消费者 int buffer = 10; //缓冲区 //1生产者 2消费者 4产品缓冲池 bool BoolEnd=false; int buffer_ni = 0, buffer_nj = 0; const int buffer_size = 4; int buffer_multi[buffer_size]{0}; const int produce_num = 10; //生产产品个数 CRITICAL_SECTION cs; HANDLE g_hEventBufferEmpty, g_hEventBufferFull; HANDLE g_hSemaphoreBufferEmpty, g_hSemaphoreBufferFull; //生产者线程函数 unsigned int _stdcall ProducerThreadFun(PVOID pM) { for (size_t i = 0; i < produce_num; i++) { //等待缓冲区为空 WaitForSingleObject(g_hEventBufferEmpty, INFINITE); //互斥的访问缓冲区 EnterCriticalSection(&cs); SetConsolecolor(FOREGROUND_RED); buffer = i; printf("生产者将第%2d件产品放入缓冲区\n", i); LeaveCriticalSection(&cs); //通知缓冲区有数据 SetEvent(g_hEventBufferFull); } return 0; } //消费者线程函数 unsigned int _stdcall ConsumerThreadFun(PVOID pM) { volatile bool flag = true; while (flag) { //等待缓冲区有数据 WaitForSingleObject(g_hEventBufferFull, INFINITE); //互斥的访问缓冲区 EnterCriticalSection(&cs); SetConsolecolor(FOREGROUND_GREEN); printf("消费者从缓冲区提取第%d件产品\n", buffer); if (produce_num == buffer + 1) { flag = false; } LeaveCriticalSection(&cs); //通知缓冲区为空 SetEvent(g_hEventBufferEmpty); Sleep(10); //产品区为空,停止提取产品(避免空转),让生产者生产产品 } return 0; } unsigned int _stdcall ProduceThreadFun_Multi(PVOID pM) { for (size_t i = 0; i < produce_num; i++) { //等待有空的缓冲区出现 WaitForSingleObject(g_hSemaphoreBufferEmpty, INFINITE); //互斥的访问缓冲区 EnterCriticalSection(&cs); SetConsolecolor(FOREGROUND_RED); buffer_multi[buffer_ni] = i; printf("生产者在缓冲池第%d个缓冲区投放数据%2d\n", buffer_ni, i); buffer_ni = (buffer_ni + 1) % buffer_size; LeaveCriticalSection(&cs); //通知消费者有新数据 ReleaseSemaphore(g_hSemaphoreBufferFull, 1, NULL); } printf("生产者完成任务,线程结束运行!\n"); return 0; } unsigned int _stdcall ConsumerThreadFun_Multi(PVOID pM) { while (true) { //等待非空的缓冲区出现 WaitForSingleObject(g_hSemaphoreBufferFull, INFINITE); //互斥的访问缓冲区 EnterCriticalSection(&cs); if (BoolEnd) //另外一个消费者线程已经读取最后一件产品 { LeaveCriticalSection(&cs); break; } SetConsolecolor(FOREGROUND_GREEN); printf(" 编号为%d的消费者从缓冲区中第%d缓冲区提取产品%2d\n", GetCurrentThreadId(), buffer_nj, buffer_multi[buffer_nj]); if (buffer_multi[buffer_nj]+1==produce_num) { BoolEnd = true; //读取结束标识符 LeaveCriticalSection(&cs); //读取最后一个产品,因为另外一个线程在等待,故添加信号量 //这样会导致重复读取最后一件产品,可添加特殊标记,此处添加BoolEnd ReleaseSemaphore(g_hSemaphoreBufferFull, 1, NULL); break; } buffer_nj = (buffer_nj + 1) % buffer_size; LeaveCriticalSection(&cs); Sleep(50); //避免空转,让生产区填充产品 ReleaseSemaphore(g_hSemaphoreBufferEmpty, 1, NULL); } SetConsolecolor(FOREGROUND_RED); printf("\n编号为%d的消费者收到通知,线程结束运行\n", GetCurrentThreadId()); return 0; } void fun1() { printf("生产者消费问题 1生产者 1消费者 \n"); //初始化临界段 InitializeCriticalSection(&cs); //创建两个自动复位事件,注意参数区别 g_hEventBufferEmpty = CreateEvent(NULL, FALSE, TRUE, NULL); g_hEventBufferFull = CreateEvent(NULL, FALSE, FALSE, NULL); const int threadnum = 2; HANDLE handle[threadnum]; handle[0] = (HANDLE)_beginthreadex(NULL, 0, ProducerThreadFun, NULL, 0, NULL); handle[1] = (HANDLE)_beginthreadex(NULL, 0, ConsumerThreadFun, NULL, 0, NULL); WaitForMultipleObjects(threadnum, handle, TRUE, INFINITE); //销毁事件和关键段 CloseHandle(handle[0]); CloseHandle(handle[1]); DeleteCriticalSection(&cs); CloseHandle(g_hEventBufferEmpty); CloseHandle(g_hEventBufferFull); } void fun2() { InitializeCriticalSection(&cs); g_hSemaphoreBufferEmpty = CreateSemaphore(NULL, 4, 4, NULL); g_hSemaphoreBufferFull = CreateSemaphore(NULL, 0, 4, NULL); const int thread_num = 3; HANDLE handle[thread_num]; //初始化变量 buffer_ni = 0; buffer_nj = 0; BoolEnd = false; memset(buffer_multi, 0, sizeof(buffer_multi)); //生产者线程 handle[0] = (HANDLE)_beginthreadex(NULL, 0, ProduceThreadFun_Multi, NULL, 0, NULL); //消费者线程 handle[1] = (HANDLE)_beginthreadex(NULL, 0, ConsumerThreadFun_Multi, NULL, 0, NULL); handle[2] = (HANDLE)_beginthreadex(NULL, 0, ConsumerThreadFun_Multi, NULL, 0, NULL); WaitForMultipleObjects(thread_num, handle, TRUE, INFINITE); for (size_t i = 0; i < thread_num; i++) { CloseHandle(handle[i]); } CloseHandle(g_hSemaphoreBufferEmpty); CloseHandle(g_hSemaphoreBufferFull); DeleteCriticalSection(&cs); } int main() { //1生产者 1消费者 //fun1(); //1生产者 2消费者 fun2(); //检测内存泄漏 _CrtDumpMemoryLeaks(); return 0; }
此题后续解决了双线程读取队列数据问题。
详见:http://blog.csdn.net/morewindows/article/details/8646902