30win32编程基础——生产者消费者问题深入理解
问题:
1个线程负责生产,1个线程负责消费。
#include <Windows.h>
#include "OutPut.h"
int share_res =0;
int Max = 10;
DWORD WINAPI ThreadProduce( __in LPVOID lpParameter)
{
for(int i=0;i<Max;i++)
{
share_res = 1;
int id = GetCurrentThreadId();
OutputDebugStringF("生产者线程%d生产数据%d放入缓冲区\n",id,share_res);
}
return 0;
}
DWORD WINAPI ThreadConsume(
__in LPVOID lpParameter
){
for(int i=0;i<Max;i++)
{
share_res = 0;
int id = GetCurrentThreadId();
OutputDebugStringF("消费者线程%d消费从缓冲区中取出数据%d\n",id,share_res);
}
return 0;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HANDLE h[2];
h[0] = CreateThread(NULL,NULL,ThreadProduce,(LPVOID)0,0,NULL);
h[1] =CreateThread(NULL,NULL,ThreadConsume,(LPVOID)1,0,NULL);
WaitForMultipleObjects(2,h,TRUE,-1);
CloseHandle(h[0]);
CloseHandle(h[1]);
system("pause");
return 0;
}
首先看什么都不用的输出结果:
生产者线程19000生产数据1放入缓冲区 消费者线程7404消费从缓冲区中取出数据0 生产者线程19000生产数据1放入缓冲区 消费者线程7404消费从缓冲区中取出数据0 生产者线程19000生产数据1放入缓冲区 消费者线程7404消费从缓冲区中取出数据0 生产者线程19000生产数据1放入缓冲区 消费者线程7404消费从缓冲区中取出数据0 生产者线程19000生产数据1放入缓冲区 消费者线程7404消费从缓冲区中取出数据0 生产者线程19000生产数据1放入缓冲区 消费者线程7404消费从缓冲区中取出数据0 生产者线程19000生产数据1放入缓冲区 消费者线程7404消费从缓冲区中取出数据0 消费者线程7404消费从缓冲区中取出数据0 生产者线程19000生产数据1放入缓冲区 生产者线程19000生产数据1放入缓冲区 消费者线程7404消费从缓冲区中取出数据0 消费者线程7404消费从缓冲区中取出数据0 生产者线程19000生产数据1放入缓冲区
可以发现,中间部分会出现先消费后生产的问题,那使用临界区能解决这个问题吗?
#include <Windows.h> #include "OutPut.h" int share_res =0; int Max = 10; CRITICAL_SECTION cs; DWORD WINAPI ThreadProduce( __in LPVOID lpParameter) { for(int i=0;i<Max;i++) { EnterCriticalSection(&cs); share_res = 1; int id = GetCurrentThreadId(); OutputDebugStringF("生产者线程%d生产数据%d放入缓冲区\n",id,share_res); LeaveCriticalSection(&cs); } return 0; } DWORD WINAPI ThreadConsume( __in LPVOID lpParameter ){ for(int i=0;i<Max;i++) { EnterCriticalSection(&cs); share_res = 0; int id = GetCurrentThreadId(); OutputDebugStringF("消费者线程%d消费从缓冲区中取出数据%d\n",id,share_res); LeaveCriticalSection(&cs); } return 0; } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { InitializeCriticalSection(&cs); HANDLE h[2]; h[0] = CreateThread(NULL,NULL,ThreadProduce,(LPVOID)0,0,NULL); h[1] =CreateThread(NULL,NULL,ThreadConsume,(LPVOID)1,0,NULL); WaitForMultipleObjects(2,h,TRUE,-1); CloseHandle(h[0]); CloseHandle(h[1]); DeleteCriticalSection(&cs); system("pause"); return 0; }
查看输出结果:
生产者线程9396生产数据1放入缓冲区 生产者线程9396生产数据1放入缓冲区 生产者线程9396生产数据1放入缓冲区 生产者线程9396生产数据1放入缓冲区 生产者线程9396生产数据1放入缓冲区 生产者线程9396生产数据1放入缓冲区 生产者线程9396生产数据1放入缓冲区 消费者线程16920消费从缓冲区中取出数据0 消费者线程16920消费从缓冲区中取出数据0 消费者线程16920消费从缓冲区中取出数据0 消费者线程16920消费从缓冲区中取出数据0 消费者线程16920消费从缓冲区中取出数据0 消费者线程16920消费从缓冲区中取出数据0 消费者线程16920消费从缓冲区中取出数据0 消费者线程16920消费从缓冲区中取出数据0 消费者线程16920消费从缓冲区中取出数据0 消费者线程16920消费从缓冲区中取出数据0 生产者线程9396生产数据1放入缓冲区 线程 'Win32 线程' (0x4218) 已退出,返回值为 0 (0x0)。 生产者线程9396生产数据1放入缓冲区 生产者线程9396生产数据1放入缓冲区 线程 'Win32 线程' (0x24b4) 已退出,返回值为 0 (0x0)。
输出是:先生产了7次,然后消费了10次,然后消费者进程先结束了。
使用临界区不行,那么使用互斥体可以实现生产者消费者问题吗?
#include <Windows.h> #include "OutPut.h" int share_res =0; int Max = 10; HANDLE hMutex; DWORD WINAPI ThreadProduce( __in LPVOID lpParameter) { for(int i=0;i<Max;i++) { WaitForSingleObject(hMutex,-1); share_res = 1; int id = GetCurrentThreadId(); OutputDebugStringF("生产者线程%d生产数据%d放入缓冲区\n",id,share_res); ReleaseMutex(hMutex); } return 0; } DWORD WINAPI ThreadConsume( __in LPVOID lpParameter ){ for(int i=0;i<Max;i++) { WaitForSingleObject(hMutex,-1); share_res = 0; int id = GetCurrentThreadId(); OutputDebugStringF("消费者线程%d消费从缓冲区中取出数据%d\n",id,share_res); ReleaseMutex(hMutex); } return 0; } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { hMutex = CreateMutex(NULL,NULL,TEXT("xyz")); HANDLE h[2]; h[0] = CreateThread(NULL,NULL,ThreadProduce,(LPVOID)0,0,NULL); h[1] =CreateThread(NULL,NULL,ThreadConsume,(LPVOID)1,0,NULL); WaitForMultipleObjects(2,h,TRUE,-1); CloseHandle(h[0]); CloseHandle(h[1]); CloseHandle(hMutex); system("pause"); return 0; }
查看输出:
生产者线程17128生产数据1放入缓冲区 消费者线程15504消费从缓冲区中取出数据0 生产者线程17128生产数据1放入缓冲区 消费者线程15504消费从缓冲区中取出数据0 生产者线程17128生产数据1放入缓冲区 消费者线程15504消费从缓冲区中取出数据0 消费者线程15504消费从缓冲区中取出数据0 生产者线程17128生产数据1放入缓冲区 消费者线程15504消费从缓冲区中取出数据0 生产者线程17128生产数据1放入缓冲区 消费者线程15504消费从缓冲区中取出数据0 生产者线程17128生产数据1放入缓冲区 消费者线程15504消费从缓冲区中取出数据0 生产者线程17128生产数据1放入缓冲区 消费者线程15504消费从缓冲区中取出数据0 生产者线程17128生产数据1放入缓冲区 消费者线程15504消费从缓冲区中取出数据0 生产者线程17128生产数据1放入缓冲区 消费者线程15504消费从缓冲区中取出数据0 生产者线程17128生产数据1放入缓冲区 线程 'Win32 线程' (0x3c90) 已退出,返回值为 0 (0x0)。 线程 'Win32 线程' (0x42e8) 已退出,返回值为 0 (0x0)。
发现互斥体也不能解决生产者和消费者问题,为什么?
因此不管是临界区还是互斥体解决的是互斥问题,即:同一时间只有1个线程访问同1个共享资源,这样就会出现这种情况,刚消费者执行完,下一步本来该生产者执行了,
但是由于某些原因(CPU调度线程,不可控),生产者线程不执行,继续执行消费者线程,然后再去执行生产者线程。这样就导致多次执行消费者线程或者多次执行生产者线程。
以上是互斥。
解决不了同步问题,即:2个线程有个先后顺序交替执行。
最简单是就是使用--->事件(或者信号量)
我们定义2个事件,1个生产者事件,1个消费者事件。
刚开始生产者线程先执行,生产者执行完通知消费者执行;消费者执行完通知生产者执行。
这样依次交替执行。
#include <Windows.h> #include "OutPut.h" int share_res =0; int Max = 10; HANDLE hProEvent; HANDLE hConEvent; DWORD WINAPI ThreadProduce( __in LPVOID lpParameter) { for(int i=0;i<Max;i++) { WaitForSingleObject(hProEvent,-1); share_res = 1; int id = GetCurrentThreadId(); OutputDebugStringF("生产者线程%d生产数据%d放入缓冲区\n",id,share_res); SetEvent(hConEvent); } return 0; } DWORD WINAPI ThreadConsume( __in LPVOID lpParameter ){ for(int i=0;i<Max;i++) { WaitForSingleObject(hConEvent,-1); share_res = 0; int id = GetCurrentThreadId(); OutputDebugStringF("消费者线程%d消费从缓冲区中取出数据%d\n",id,share_res); SetEvent(hProEvent); } return 0; } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { hProEvent = CreateEvent(NULL,FALSE,TRUE,TEXT("Procude"));//刚开始发信号,,遇到wait自动变为未通知状态 hConEvent = CreateEvent(NULL,FALSE,FALSE,TEXT("Consume"));//刚开始不发信号,遇到wait自动变为未通知状态 HANDLE h[2]; h[0] = CreateThread(NULL,NULL,ThreadProduce,(LPVOID)0,0,NULL); h[1] =CreateThread(NULL,NULL,ThreadConsume,(LPVOID)1,0,NULL); WaitForMultipleObjects(2,h,TRUE,-1); CloseHandle(h[0]); CloseHandle(h[1]); CloseHandle(hProEvent); CloseHandle(hConEvent); system("pause"); return 0; }
查看输出结果:
生产者线程19516生产数据1放入缓冲区
消费者线程18396消费从缓冲区中取出数据0
生产者线程19516生产数据1放入缓冲区
消费者线程18396消费从缓冲区中取出数据0
生产者线程19516生产数据1放入缓冲区
消费者线程18396消费从缓冲区中取出数据0
生产者线程19516生产数据1放入缓冲区
消费者线程18396消费从缓冲区中取出数据0
生产者线程19516生产数据1放入缓冲区
消费者线程18396消费从缓冲区中取出数据0
生产者线程19516生产数据1放入缓冲区
消费者线程18396消费从缓冲区中取出数据0
生产者线程19516生产数据1放入缓冲区
消费者线程18396消费从缓冲区中取出数据0
生产者线程19516生产数据1放入缓冲区
消费者线程18396消费从缓冲区中取出数据0
生产者线程19516生产数据1放入缓冲区
消费者线程18396消费从缓冲区中取出数据0
生产者线程19516生产数据1放入缓冲区
消费者线程18396消费从缓冲区中取出数据0
线程 'Win32 线程' (0x4c3c) 已退出,返回值为 0 (0x0)。
线程 'Win32 线程' (0x47dc) 已退出,返回值为 0 (0x0)。
事件只能解决简单的线程同步问题,对于复杂的线程同步控制还解决不了问题,因此又引出信号量的问题。
还一种办法,使用标志法:
定义1个flag判断是否消费和生产。
#include <Windows.h> #include "OutPut.h" int share_res =0; int Max = 10; int flag = 1; DWORD WINAPI ThreadProduce( __in LPVOID lpParameter) { int id = GetCurrentThreadId(); for(int i=0;i<Max;i++) { if(flag) { share_res = 1; OutputDebugStringF("生产者线程%d第%d次生产数据%d放入缓冲区\n",id,i,share_res); flag=0; }else{ OutputDebugStringF("生产者线程%d第%d次等待生产\n",id,i); Sleep(50); } } return 0; } DWORD WINAPI ThreadConsume( __in LPVOID lpParameter ){ int id = GetCurrentThreadId(); for(int i=0;i<Max;i++) { if(flag == 0) { share_res = 0; OutputDebugStringF("消费者线程%d消费从缓冲区中取出数据%d\n",id,share_res); flag=1; }else { OutputDebugStringF("消费者线程%d第%d次等待消费\n",id,i); Sleep(50); } } return 0; } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HANDLE h[2]; h[0] = CreateThread(NULL,NULL,ThreadProduce,(LPVOID)0,0,NULL); h[1] = CreateThread(NULL,NULL,ThreadConsume,(LPVOID)1,0,NULL); WaitForMultipleObjects(2,h,TRUE,-1); CloseHandle(h[0]); CloseHandle(h[1]); system("pause"); return 0; }
输出结果:
消费者线程15968第0次等待消费 生产者线程19416第0次生产数据1放入缓冲区 生产者线程19416第1次等待生产 生产者线程19416第2次等待生产 消费者线程15968消费从缓冲区中取出数据0 消费者线程15968第2次等待消费 生产者线程19416第3次生产数据1放入缓冲区 消费者线程15968第3次等待消费 生产者线程19416第4次等待生产 消费者线程15968消费从缓冲区中取出数据0 生产者线程19416第5次等待生产 消费者线程15968第5次等待消费 消费者线程15968第6次等待消费 生产者线程19416第6次生产数据1放入缓冲区 生产者线程19416第7次等待生产 生产者线程19416第8次等待生产 消费者线程15968消费从缓冲区中取出数据0 消费者线程15968第8次等待消费 生产者线程19416第9次生产数据1放入缓冲区 线程 'Win32 线程' (0x4bd8) 已退出,返回值为 0 (0x0)。 消费者线程15968消费从缓冲区中取出数据0
多几次执行结果:
生产者线程3724第0次生产数据1放入缓冲区
生产者线程3724第1次等待生产
消费者线程8640第0次等待消费
消费者线程8640消费第1次从缓冲区中取出数据0
生产者线程3724第2次等待生产
消费者线程8640第2次等待消费
消费者线程8640第3次等待消费
生产者线程3724第3次生产数据1放入缓冲区
生产者线程3724第4次等待生产
生产者线程3724第5次等待生产
消费者线程8640消费第4次从缓冲区中取出数据0
消费者线程8640第5次等待消费
生产者线程3724第6次生产数据1放入缓冲区
生产者线程3724第7次等待生产
消费者线程8640消费第6次从缓冲区中取出数据0
消费者线程8640第7次等待消费
生产者线程3724第8次生产数据1放入缓冲区
生产者线程3724第9次等待生产
消费者线程8640消费第8次从缓冲区中取出数据0
消费者线程8640第9次等待消费

浙公网安备 33010602011771号