大学操作系统教材里讲的最多的估计就是信号量Semaphore了,具体就不再介绍了,通常用来处理多线程访问多个资源的情况。
实际上,如果创建一个信号量,并且它的最大计数是1,那么它就与Mutex等价。
下面是个生产者-消费者问题的Win32程序,运行时的截图如下:
代码如下:
/* 生产者-消费者问题是一个经典的进程同步问题,该问题最早由Dijkstra提出;很多计算机问题都可以抽象为生产者-消费者问题。
* 有1个生产者生产商品放到环形buffer中供5个消费者消费;生产者每次最多生产5个商品,消费者每次消费1个。
* 要解决这个问题,我们必须确保:(1)并且当缓冲区中没有商品时,消费者不能消费,缓冲区满时,生产者也不能生产商品 (2)不同消费者 * 不能同时消费同一个商品;
*
* 解决(1)我们用一个生产者信号量表示生产者者资源,即空闲buffer数量;用一个消费者信号量表示消费者资源,即非空闲buffer数量。
* 解决(2)我们用一个互斥量Mutex,得到这个Mutex的消费者才能消费。
*/
#include <time.h>
#include <stdlib.h>
#include <Windows.h>
#define ASSERT(a) if (!(a)) \
exit(EXIT_FAILURE)
#define MAX_PRODUCE_COUNT 5 //生产者每次最多生产数量
#define CONSUMER_COUNT 5 //消费者数量
#define BUFFER_SIZE 20 //缓冲区大小
#define SLEEP_TIME 600
#define WM_FORCE_PAINT (WM_APP+10)
void ProduceAndConsume();
void EndProduceConsume();
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM) ; //Win32窗口回调函数
DWORD WINAPI ProducerThread(LPVOID pVoid); //生产者线程函数
DWORD WINAPI ConsumerThread(LPVOID pVoid); //消费者线程函数
int iProducerPointer; //生产者指针,指向可以放商品的的位置
int iConsumerPointer; //消费者指针,指向可以消费商品的位置
HANDLE hProducerSemaphore; //生产者信号量,初始有20个资源
HANDLE hConsumerSemaphore; //消费者信号量,初始有0个资源
HANDLE hConsumerMutex; //生产者Mutex
HANDLE hProducerThread; //生产者线程,不断生产商品
HANDLE hConsumersThread[CONSUMER_COUNT]; //消费者线程,不断消费商品
HWND hWnd;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("生长者消费者") ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground= (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName= szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox ( NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hWnd = CreateWindow( szAppName,
TEXT ("生长者消费者"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL) ;
ShowWindow (hWnd, iCmdShow) ;
UpdateWindow (hWnd) ;
ProduceAndConsume(); //创建生产者消费者线程、信号量、Mutex,并运行
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
EndProduceConsume();
return (int)msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int iTemp;
int iXStart,iYStart;
HDC hdc ;
HBRUSH hBrush;
PAINTSTRUCT ps ;
RECT rect;
MSG msg;
switch (message)
{
case WM_CREATE:
return 0 ;
case WM_FORCE_PAINT:
InvalidateRect(hWnd, NULL, TRUE);
while (PeekMessage(&msg, hWnd, WM_FORCE_PAINT, WM_FORCE_PAINT, PM_REMOVE))
;
return 0;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
GetClientRect(hWnd,&rect);
iXStart = (rect.right-rect.left)/2-200;
iYStart = (rect.bottom-rect.top)/2-10;
hBrush = SelectObject(hdc, (HBRUSH)GetStockObject(GRAY_BRUSH));
iTemp = iConsumerPointer;
while (TRUE)
{
Rectangle(hdc, iXStart+iTemp*20, iYStart, iXStart+(iTemp+1)*20, iYStart+20);
if (++iTemp >= BUFFER_SIZE)
iTemp = 0;
if (iTemp == iProducerPointer)
break;
}
SelectObject(hdc, hBrush);
while (TRUE)
{
Rectangle(hdc, iXStart+iTemp*20, iYStart, iXStart+(iTemp+1)*20, iYStart+20);
if (++iTemp >= BUFFER_SIZE)
iTemp = 0;
if (iTemp == iConsumerPointer)
break;
}
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
DWORD WINAPI ProducerThread(LPVOID pVoid)
{
int i;
int iRandom;
while (TRUE)
{
srand((unsigned)time(NULL));
iRandom = rand()%MAX_PRODUCE_COUNT;
if (iRandom == 0)
iRandom++;
//生产者申请iRandom个资源
for (i=0; i<iRandom; i++)
ASSERT( WAIT_OBJECT_0 == WaitForSingleObject(hProducerSemaphore, INFINITE) );
//生产者生产iRandom个商品
iProducerPointer = iProducerPointer+iRandom;
if (iProducerPointer>=BUFFER_SIZE)
iProducerPointer = iProducerPointer-BUFFER_SIZE;
SendMessage(hWnd, WM_FORCE_PAINT, 0, 0);
Sleep(SLEEP_TIME);
//生产者生产了iRandom个商品,消费者有更多的商品消费了;所以为消费者释放iRandom个资源
ASSERT(ReleaseSemaphore(hConsumerSemaphore, (long)iRandom, NULL));
}
return 0;
}
DWORD WINAPI ConsumerThread(LPVOID pVoid)
{
while (TRUE)
{
//消费者申请到Semaphore和Mutex后,才能消费
ASSERT( WAIT_OBJECT_0 == WaitForSingleObject(hConsumerSemaphore, INFINITE) );
ASSERT( WAIT_OBJECT_0 == WaitForSingleObject(hConsumerMutex, INFINITE) );
//消费者消费一个商品
iConsumerPointer++;
if (iConsumerPointer>=BUFFER_SIZE)
iConsumerPointer = 0;
SendMessage(hWnd, WM_FORCE_PAINT, 0, 0);
Sleep(SLEEP_TIME/2);
//消费者释放Mutex
ASSERT(ReleaseMutex(hConsumerMutex));
//消费者消费了一个商品,buffer中多了一个空闲位置,为生产者释放一个资源
ASSERT(ReleaseSemaphore(hProducerSemaphore, (long)1, NULL));
}
return 0;
}
void ProduceAndConsume()
{
int i;
DWORD dwThreadID;
iProducerPointer = 0;
iConsumerPointer = 0;
hProducerSemaphore = CreateSemaphore(NULL, BUFFER_SIZE, BUFFER_SIZE, NULL); //创建生产者信号量,初始有20个资源
hConsumerSemaphore = CreateSemaphore(NULL, 0, BUFFER_SIZE, NULL); //创建消费者信号量,初始有0个资源
hConsumerMutex = CreateMutex(NULL, FALSE, NULL); //创建消费者Mutex
hProducerThread = CreateThread(NULL, 0, ProducerThread, NULL, 0, &dwThreadID);
for (i=0; i<CONSUMER_COUNT; i++)
{
hConsumersThread[i] = CreateThread(NULL, 0, ConsumerThread, NULL, 0, &dwThreadID);
}
}
void EndProduceConsume()
{
int i;
ASSERT(CloseHandle(hProducerSemaphore));
ASSERT(CloseHandle(hConsumerSemaphore));
ASSERT(CloseHandle(hConsumerMutex));
ASSERT(CloseHandle(hProducerThread));
for (i=0; i<CONSUMER_COUNT; i++)
{
ASSERT(CloseHandle(hConsumersThread[i]));
}
}