大学操作系统教材里讲的最多的估计就是信号量Semaphore了,具体就不再介绍了,通常用来处理多线程访问多个资源的情况。
实际上,如果创建一个信号量,并且它的最大计数是1,那么它就与Mutex等价。
下面是个生产者-消费者问题的Win32程序,运行时的截图如下:
代码如下:
实际上,如果创建一个信号量,并且它的最大计数是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]));
}
}
* 有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]));
}
}