Win32API クリティカルセクションとイベントを用いた生産者・消費者パターン

マルチスレッドプログラミングの代表的なデザインパターンである「生産者・消費者パターン」をWin32 APIで実現する。

#include <windows.h>
#include <process.h>
#include <stdio.h>

const int MaxRequest = 10;
int iQueue[MaxRequest];//リクエストを溜めておくキュー(待ち行列)
int head = 0;//キューの先頭を表すインデックス
int tail = 0;//キューの末尾を表すインデックス
int count = 0;//キューに格納されているリクエストの数
HANDLE hEventCons;//生産者用イベントオブジェクト
HANDLE hEventProd;//消費者用イベントオブジェクト
CRITICAL_SECTION cs;//スレッドを同期させるためのクリティカルセクション


//キューを介して生産者スレッドが消費者スレッドにリクエスト(整数値)を送るための関数(生産者スレッドから呼ばれる)
void PutRequest(int n)
{
	EnterCriticalSection(&cs);

        //キューに格納されたリクエストの数が最大値に達した場合は
        //クリティカルセクションをアンロックし待機状態に入る
        //待機状態から脱出したら、再度ロックを取りwhileループの
        //継続条件のテストを実施する
	while (count >= MaxRequest) {
		LeaveCriticalSection(&cs);
		WaitForSingleObject(hEventProd, INFINITE);
		EnterCriticalSection(&cs);
	}
	
        //キューにリクエストを格納する(エンキュー)
	iQueue[tail] = n;
	tail = (tail + 1) % MaxRequest;
	count++;

        //消費者スレッドにリクエストがキューに格納されたことを通知する
	SetEvent(hEventCons);

	LeaveCriticalSection(&cs);
}

//キューを介して消費者スレッドが生産者スレッドからリクエスト(整数値)を取り出すための関数(消費者スレッドから呼ばれる)
int TakeRequest()
{
	EnterCriticalSection(&cs);

        //キューが空の場合は、クリティカルセクションをアンロックし
        //待機状態に入る。待機状態から脱出したら、再度ロックを取り
        //whileループの継続条件のテストを実施する
	while (count <= 0) {
		LeaveCriticalSection(&cs);
		WaitForSingleObject(hEventCons, INFINITE);
		EnterCriticalSection(&cs);
	}

        //キューからリクエストを取り出す(デキュー)
 	int n = iQueue[head];
	head = (head + 1) % MaxRequest;
	count--;

        //生産者スレッドにキューからリクエストが取り出されたことを通知する
	SetEvent(hEventProd);

	LeaveCriticalSection(&cs);

	return n;
}

//生産者スレッド
//キューを介して、消費者スレッドに対しリクエスト(整数値)を送る
unsigned int WINAPI ProducerThread(LPVOID arg)
{
	int n = 0;
	while (true) {
		printf("PutRequest: %d\n", n);
		PutRequest(n++);
		Sleep(rand() % 1000);
	}
	return 0;
}

//消費者スレッド
//キューを介して生産者スレッドから送られたリクエスト(整数値)
//を取り出し、コンソールに表示する
unsigned int WINAPI ConsumerThread(LPVOID arg)
{
	while (true) {
		printf("TakeRequest: %d\n", TakeRequest());
	}
	return 0;
}

int main()
{
        //生産者スレッド、消費者スレッドを起動する
	const int NumberOfConsThreads = 2;
	const int NumberOfProdThreads = 10;
	HANDLE hThreadCons[NumberOfConsThreads];
	HANDLE hThreadProd[NumberOfProdThreads];
	unsigned int uiConsThreadId;
	unsigned int uiProdThreadId;

	//Initialization
	InitializeCriticalSection(&cs);
	hEventCons = CreateEvent(NULL, TRUE, FALSE, "Consumer");
	hEventProd = CreateEvent(NULL, TRUE, FALSE, "Producer");

	//Create consumer threads
	for (int i = 0; i < NumberOfConsThreads; i++) {
		hThreadCons[i] = (HANDLE)_beginthreadex(NULL,
			0,
			ConsumerThread,
			NULL,
			0,
			&uiConsThreadId);
	}

	//Create producer threads
	for (int i = 0; i < NumberOfProdThreads; i++) {
		hThreadProd[i] = (HANDLE)_beginthreadex(NULL,
			0,
			ProducerThread,
			NULL,
			0,
			&uiProdThreadId);
	}

	//Wait for exit consumer threads
	for (int i = 0; i < NumberOfConsThreads; i++) {
		WaitForSingleObject(hThreadCons[i], INFINITE);
	}

	//Wait for exit producer threads
	for (int i = 0; i < NumberOfProdThreads; i++) {
		WaitForSingleObject(hThreadProd[i], INFINITE);
	}

	//Cleanup
	DeleteCriticalSection(&cs);
	CloseHandle(hEventCons);
	CloseHandle(hEventProd);
	
	return 0;
}