先来描述一下待解决的问题:
有一个仓库,它最多有七个槽位,最开始每个槽位都是空的。当有空槽位的时候,允许生产者往里面放东西。当槽位上有东西时,允许消费者从里面拿东西;满了则不允许再放,空了则不允许再拿。因为仓库设计问题,同一时间内,只允许一个人进去放东西或者拿东西。需要尽最大的效率安排生产者和消费者的工作。可以看到,这里生产和消费的动作不需要做严格的同步,只要规则允许,可以连续生成三次,也可以连续消费三次。而没有说一定要生产一次,消费一次,再生产一次,再消费一次的顺序来。
// productor_consumer.cpp
// Windows 平台对生产者-消费者问题的解决方案
#include <windows.h>
#include <tchar.h>
#include <list>
// 定义仓库的最大槽位
#define WH_SLOT 7
// 定义等待信号灯的超时时间
#define TIMEOUT 3000
// 定义仓库类
class Warehource
{
public:
Warehource()
{
// 创建一个访问仓库的互斥量,生产者和消费者不能同时访问仓库
m_hAccess = CreateEvent(NULL, FALSE, TRUE, NULL);
}
~Warehource()
{
// 关闭访问互斥量
CloseHandle(m_hAccess);
}
// 放入到仓库
BOOL put(char ch)
{
// 尝试获取互斥量
if (WAIT_OBJECT_0 == WaitForSingleObject(m_hAccess, 100))
{
m_slot.push_back(ch);
printf("Product an item, now warehourse size is %d\n", m_slot.size());
SetEvent(m_hAccess); //let's next access available
return TRUE;
}
return FALSE;
}
// 从仓库取出
BOOL get(char* ch)
{
// 尝试获取互斥量
if (WAIT_OBJECT_0 == WaitForSingleObject(m_hAccess, 100))
{
*ch = m_slot.front();
m_slot.pop_front();
printf("Consume an item, now warehourse size is %d\n", m_slot.size());
SetEvent(m_hAccess); //let's next access available
return TRUE;
}
return FALSE;
}
private:
std::list<char> m_slot;
HANDLE m_hAccess; //对仓库访问要互斥
};
DWORD WINAPI ProductorRun(LPVOID ud);
DWORD WINAPI ConsumerRun(LPVOID ud);
HANDLE g_hProdExit; //生产者退出的信号
HANDLE g_hFreeSemaphore; //仓库空闲槽的信号灯,最大值为WH_SLOT
HANDLE g_hConsExit; //消费者退出的信号
HANDLE g_hDataSemaphore; //仓库可用槽的信号灯,最大值为WH_SLOT
int _tmain(int argc, _TCHAR* argv[])
{
Warehource wh;
g_hProdExit = CreateEvent(NULL, FALSE, FALSE, NULL);
g_hConsExit = CreateEvent(NULL, FALSE, FALSE, NULL);
//仓库空闲槽的信号灯,最大值为WH_SLOT,初始值为WH_SLOT,因为仓库
//一开始都是空的
g_hFreeSemaphore = CreateSemaphore(NULL, WH_SLOT, WH_SLOT, NULL);
//仓库可用槽的信号灯,最大值为WH_SLOT,初始值为0,因为仓库
//一开始都是空的
g_hDataSemaphore = CreateSemaphore(NULL, 0, WH_SLOT, NULL);
// 创建生产者和消费者
HANDLE hProductor = CreateThread(NULL, NULL, ProductorRun, &wh, 0, 0);
HANDLE hConsumer = CreateThread(NULL, NULL, ConsumerRun, &wh, 0, 0);
// let main thread sleep 30 seconds
Sleep(3000);
// 结束生产者
SetEvent(g_hProdExit);
WaitForSingleObject(hProductor, INFINITE);
CloseHandle(g_hProdExit);
CloseHandle(hProductor);
// 结束消费者
SetEvent(g_hConsExit);
WaitForSingleObject(hConsumer, INFINITE);
CloseHandle(g_hConsExit);
CloseHandle(hConsumer);
// 关闭信号灯
CloseHandle(g_hFreeSemaphore);
CloseHandle(g_hDataSemaphore);
return 0;
}
DWORD WINAPI ProductorRun(LPVOID ud)
{
BOOL fDone = FALSE;
HANDLE h2[] = { g_hProdExit, g_hFreeSemaphore };
DWORD dwWait = 0;
Warehource* wh = (Warehource*)ud;
while (!fDone)
{
// 等待退出信号或空闲信号
dwWait = WaitForMultipleObjects(2, h2, FALSE, TIMEOUT);
if (dwWait == WAIT_OBJECT_0)
{
//退出信号
fDone = TRUE;
}
else if (dwWait == WAIT_OBJECT_0+1)
{
// 空闲信号,表示仓库中有空闲槽,尝试放入到仓库
if (wh->put('a'))
{
// 已经成功放入一个到仓库,则置上一个可用槽的信号灯
ReleaseSemaphore(g_hDataSemaphore, 1, NULL);
}
else //访问仓库超时
{
// 将熄灭的空闲槽信号灯置回去
ReleaseSemaphore(g_hFreeSemaphore, 1, NULL);
}
}
}
return 0;
}
DWORD WINAPI ConsumerRun(LPVOID ud)
{
BOOL fDone = FALSE;
HANDLE h2[] = { g_hConsExit, g_hDataSemaphore };
DWORD dwWait = 0;
Warehource* wh = (Warehource*)ud;
while (!fDone)
{
// 等待退出信号或可用信号
dwWait = WaitForMultipleObjects(2, h2, FALSE, TIMEOUT);
if (dwWait == WAIT_OBJECT_0)
{
//退出信号
fDone = TRUE;
}
else if (dwWait == WAIT_OBJECT_0+1)
{
// 可用信号,表示仓库中某个槽已有数据,尝试从中取用
char ch;
if (wh->get(&ch))
{
// 已经成功从仓库取出一个,则置上一个空闲槽的信号灯
ReleaseSemaphore(g_hFreeSemaphore, 1, NULL);
}
else //访问仓库超时
{
// 将熄灭的可用槽信号灯置回去
ReleaseSemaphore(g_hDataSemaphore, 1, NULL);
}
}
}
return 0;
}
注意semaphore是可以让多个线程同时访问共享资源的唯一信号类型。