使用事件和互斥量解决线程同步问题的经典问题——生产者和消费者问题
创建2个线程,生产者线程和消费者线程。
生产者线程负责生产产品,1次生产1个产品,生产的产品放入缓冲区,当缓冲区满了时,生产者暂停生产,等待消费者消费缓冲区中的产品再重新启动。因此,生产者生产产品前,需要等待缓冲区不满的消息。
消费者线程负责消费缓冲区中的产品,1次消费1个产品,当缓冲区为空时,消费者暂停消费,等生产者向缓冲区中放入新的产品后再重新启动。因此,消费者消费产品之前,需要等待缓冲区不为空的消息。
由于生产和消费时都会对缓冲区进行操作,因此,添加互斥量对线程进行互斥保护。
VC6.0下的C++代码如下:
#include <windows.h>
#include <stdio.h>
#define MAXPRODUCTCNT 100 // Max product Count
#define BUFSIZE 4 // product buffer size
HANDLE g_hThreads[2]; //Thread handles, Produce thread and consume thread
HANDLE g_hNotFullEvent = NULL; // Product buffer is not full event
HANDLE g_hNotEmptyEvent = NULL; // product buffer is not empty event
HANDLE g_hMutex = NULL; // Mutex
int g_iStorageCnt = 0; // product count in buffer
int g_iProductIdx = 0; // Index of the last produced product
int g_iConsumeIdx = 0; // Index of the last consumed product
DWORD WINAPI ProduceThreadProc(LPVOID lpParam)
{
DWORD dwWaitResult = 0;
// two objects.
// Mutex and Not full event.
HANDLE hObjects[2] = {NULL, NULL};
hObjects[0] = g_hMutex;
hObjects[1] = g_hNotFullEvent;
for (int iIdx = 0; iIdx < MAXPRODUCTCNT; iIdx++)
{
//Wait till Mutex and Not full event are set signaled.
dwWaitResult = WaitForMultipleObjects(
2, // number of handles in array
hObjects, // array of handles
TRUE, // wait till all are signaled
INFINITE); // indefinite wait
if (WAIT_OBJECT_0 != dwWaitResult)
{
printf("Wait error: (%d)\n", GetLastError());
ExitThread(0);
}
// Create a Product
g_iProductIdx++;
g_iStorageCnt++;
printf("Create Product %d\n", g_iProductIdx);
// check whether product buffer is full
if (g_iStorageCnt >= BUFSIZE)
{
if (!ResetEvent(g_hNotFullEvent) )
{
printf("SetEvent failed (%d)\n", GetLastError());
ExitThread(0);
}
}
// set not empty event signaled.
if (!SetEvent(g_hNotEmptyEvent) )
{
printf("SetEvent failed (%d)\n", GetLastError());
ExitThread(0);
}
// Release mutex
ReleaseMutex(g_hMutex);
}
return 1;
}
DWORD WINAPI ConsumeThreadProc(LPVOID lpParam)
{
DWORD dwWaitResult = 0;
// two objects.
// Mutex and Not empty event.
HANDLE hObjects[2] = {NULL, NULL};
hObjects[0] = g_hMutex;
hObjects[1] = g_hNotEmptyEvent;
for (int iIdx = 0; iIdx < MAXPRODUCTCNT; iIdx++)
{
//Wait till Mutex and Not empty event are set signaled.
dwWaitResult = WaitForMultipleObjects(
2, // number of handles in array
hObjects, // array of handles
TRUE, // wait till all are signaled
INFINITE); // indefinite wait
if (WAIT_OBJECT_0 != dwWaitResult)
{
printf("Wait error: (%d)\n", GetLastError());
ExitThread(0);
}
// Consume a Product
g_iStorageCnt--;
g_iConsumeIdx++;
printf("Consume product %d(%d)\n", g_iConsumeIdx, g_iProductIdx);
// check whether product buffer is empty
if (g_iStorageCnt < 1)
{
if (!ResetEvent(g_hNotEmptyEvent) )
{
printf("SetEvent failed (%d)\n", GetLastError());
ExitThread(0);
}
}
/*set not full event signaled*/
if (!SetEvent(g_hNotFullEvent) )
{
printf("SetEvent failed (%d)\n", GetLastError());
ExitThread(0);
}
//Release mutex
ReleaseMutex(g_hMutex);
}
return 1;
}
void main()
{
DWORD dwThreadID[2] = {0, 0};
// Create a manual-reset event object.
// set this to signaled when the product buffer is not empty.
g_hNotEmptyEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // initial state is nonsignaled
NULL // unnamed Event
);
if (NULL == g_hNotEmptyEvent)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
// Create a manual-reset event object.
// set this to signaled when the product buffer is not full.
g_hNotFullEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
TRUE, // initial state is signaled
NULL // unnamed Event
);
if (NULL == g_hNotFullEvent)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
// Create a mutex with no initial owner
g_hMutex = CreateMutex(
NULL, // default security attributes
FALSE, // initially not owned
NULL); // unnamed mutex
if (NULL == g_hMutex)
{
printf("CreateMutex error: (%d)\n", GetLastError());
return;
}
// Create a thread to produce products.
g_hThreads[0] = CreateThread(NULL,
0,
ProduceThreadProc,
NULL,
0,
&dwThreadID[0]);
if (NULL == g_hThreads[0])
{
printf("CreateThread failed (%d)\n", GetLastError());
return;
}
// Create a thred to consume products.
g_hThreads[1] = CreateThread(NULL,
0,
ConsumeThreadProc,
NULL,
0,
&dwThreadID[1]);
if (g_hThreads[1] == NULL)
{
printf("CreateThread failed (%d)\n", GetLastError());
return;
}
DWORD dwWaitResult = WaitForMultipleObjects(
2, // number of handles in array
g_hThreads, // array of thread handles
TRUE, // wait till all threads quit
INFINITE); // indefinite wait
if (WAIT_OBJECT_0 != dwWaitResult)
{
printf("Wait error : (%d)\n", GetLastError());
return;
}
CloseHandle(g_hThreads[0]);
CloseHandle(g_hThreads[1]);
CloseHandle(g_hNotFullEvent);
CloseHandle(g_hNotEmptyEvent);
CloseHandle(g_hMutex);
}
#include <stdio.h>
#define MAXPRODUCTCNT 100 // Max product Count
#define BUFSIZE 4 // product buffer size
HANDLE g_hThreads[2]; //Thread handles, Produce thread and consume thread
HANDLE g_hNotFullEvent = NULL; // Product buffer is not full event
HANDLE g_hNotEmptyEvent = NULL; // product buffer is not empty event
HANDLE g_hMutex = NULL; // Mutex
int g_iStorageCnt = 0; // product count in buffer
int g_iProductIdx = 0; // Index of the last produced product
int g_iConsumeIdx = 0; // Index of the last consumed product
DWORD WINAPI ProduceThreadProc(LPVOID lpParam)
{
DWORD dwWaitResult = 0;
// two objects.
// Mutex and Not full event.
HANDLE hObjects[2] = {NULL, NULL};
hObjects[0] = g_hMutex;
hObjects[1] = g_hNotFullEvent;
for (int iIdx = 0; iIdx < MAXPRODUCTCNT; iIdx++)
{
//Wait till Mutex and Not full event are set signaled.
dwWaitResult = WaitForMultipleObjects(
2, // number of handles in array
hObjects, // array of handles
TRUE, // wait till all are signaled
INFINITE); // indefinite wait
if (WAIT_OBJECT_0 != dwWaitResult)
{
printf("Wait error: (%d)\n", GetLastError());
ExitThread(0);
}
// Create a Product
g_iProductIdx++;
g_iStorageCnt++;
printf("Create Product %d\n", g_iProductIdx);
// check whether product buffer is full
if (g_iStorageCnt >= BUFSIZE)
{
if (!ResetEvent(g_hNotFullEvent) )
{
printf("SetEvent failed (%d)\n", GetLastError());
ExitThread(0);
}
}
// set not empty event signaled.
if (!SetEvent(g_hNotEmptyEvent) )
{
printf("SetEvent failed (%d)\n", GetLastError());
ExitThread(0);
}
// Release mutex
ReleaseMutex(g_hMutex);
}
return 1;
}
DWORD WINAPI ConsumeThreadProc(LPVOID lpParam)
{
DWORD dwWaitResult = 0;
// two objects.
// Mutex and Not empty event.
HANDLE hObjects[2] = {NULL, NULL};
hObjects[0] = g_hMutex;
hObjects[1] = g_hNotEmptyEvent;
for (int iIdx = 0; iIdx < MAXPRODUCTCNT; iIdx++)
{
//Wait till Mutex and Not empty event are set signaled.
dwWaitResult = WaitForMultipleObjects(
2, // number of handles in array
hObjects, // array of handles
TRUE, // wait till all are signaled
INFINITE); // indefinite wait
if (WAIT_OBJECT_0 != dwWaitResult)
{
printf("Wait error: (%d)\n", GetLastError());
ExitThread(0);
}
// Consume a Product
g_iStorageCnt--;
g_iConsumeIdx++;
printf("Consume product %d(%d)\n", g_iConsumeIdx, g_iProductIdx);
// check whether product buffer is empty
if (g_iStorageCnt < 1)
{
if (!ResetEvent(g_hNotEmptyEvent) )
{
printf("SetEvent failed (%d)\n", GetLastError());
ExitThread(0);
}
}
/*set not full event signaled*/
if (!SetEvent(g_hNotFullEvent) )
{
printf("SetEvent failed (%d)\n", GetLastError());
ExitThread(0);
}
//Release mutex
ReleaseMutex(g_hMutex);
}
return 1;
}
void main()
{
DWORD dwThreadID[2] = {0, 0};
// Create a manual-reset event object.
// set this to signaled when the product buffer is not empty.
g_hNotEmptyEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // initial state is nonsignaled
NULL // unnamed Event
);
if (NULL == g_hNotEmptyEvent)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
// Create a manual-reset event object.
// set this to signaled when the product buffer is not full.
g_hNotFullEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
TRUE, // initial state is signaled
NULL // unnamed Event
);
if (NULL == g_hNotFullEvent)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
// Create a mutex with no initial owner
g_hMutex = CreateMutex(
NULL, // default security attributes
FALSE, // initially not owned
NULL); // unnamed mutex
if (NULL == g_hMutex)
{
printf("CreateMutex error: (%d)\n", GetLastError());
return;
}
// Create a thread to produce products.
g_hThreads[0] = CreateThread(NULL,
0,
ProduceThreadProc,
NULL,
0,
&dwThreadID[0]);
if (NULL == g_hThreads[0])
{
printf("CreateThread failed (%d)\n", GetLastError());
return;
}
// Create a thred to consume products.
g_hThreads[1] = CreateThread(NULL,
0,
ConsumeThreadProc,
NULL,
0,
&dwThreadID[1]);
if (g_hThreads[1] == NULL)
{
printf("CreateThread failed (%d)\n", GetLastError());
return;
}
DWORD dwWaitResult = WaitForMultipleObjects(
2, // number of handles in array
g_hThreads, // array of thread handles
TRUE, // wait till all threads quit
INFINITE); // indefinite wait
if (WAIT_OBJECT_0 != dwWaitResult)
{
printf("Wait error : (%d)\n", GetLastError());
return;
}
CloseHandle(g_hThreads[0]);
CloseHandle(g_hThreads[1]);
CloseHandle(g_hNotFullEvent);
CloseHandle(g_hNotEmptyEvent);
CloseHandle(g_hMutex);
}