題目大意
有1個資源區,2個緩衝區(大小爲1),4個喫貨,總共有5個線程,其中一個線程從資源區取字母放到緩衝區,哪個緩衝區是空的就往哪放;4個喫貨線程,從緩衝區喫字母。
該題目要求實現對緩衝區的互斥訪問,緩衝區和資源區必須滿足“緩衝區爲空,才能從資源區取字母”的同步要求,緩衝區和喫貨線程要滿足“緩衝區不空,喫貨才能從緩衝區取字母”的同步要求;除此之外,還要求使用信號量控制喫貨線程最大併發數爲2.
運行結果
代碼
// EatChar.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
HANDLE evtBuffer1Empty, evtBuffer2Empty, evtBuffer1Full, evtBuffer2Full;
HANDLE hSemaphore;
char szBufferRes[100];
char szBuffer1[2], szBuffer2[2];
char szBufferEat[4][100];
HWND hEditEat[4];
HWND hEditRes, hEditBuffer1, hEditBuffer2;
// 負責從資源區取字符,放到緩衝區
// 使用事件實現對緩衝區的互斥訪問
// 不使用MUTEX或CRITICAL_SECTION是因爲事件能實現同步
// 同步的意思是要確保緩衝區爲空才往裏面存字符
DWORD WINAPI ThreadCharToBuffer(LPVOID)
{
// 只要資源區還有東西,就一直取
while (szBufferRes[0] != 0)
{
Sleep(500);
// 先看看緩衝區1是否爲空,不等待立即返回
DWORD dwRet = WaitForSingleObject(evtBuffer1Empty, 0);
// 如果1空,那麼往1放一個字符
if (dwRet == WAIT_OBJECT_0)
{
szBuffer1[0] = szBufferRes[0];
//strcpy(szBufferRes, szBufferRes+1);
memcpy(szBufferRes, szBufferRes+1, strlen(szBufferRes));
SetWindowText(hEditRes, szBufferRes);
SetWindowText(hEditBuffer1, szBuffer1);
SetEvent(evtBuffer1Full);
continue;
}
else
{
// 1還有東西,嘗試往2裏放
dwRet = WaitForSingleObject(evtBuffer2Empty, 0);
if (dwRet == WAIT_OBJECT_0)
{
szBuffer2[0] = szBufferRes[0];
//strcpy(szBufferRes, szBufferRes+1);
memcpy(szBufferRes, szBufferRes+1, strlen(szBufferRes));
SetWindowText(hEditRes, szBufferRes);
SetWindowText(hEditBuffer2, szBuffer2);
SetEvent(evtBuffer2Full);
}
}
}
return 0;
}
// 4個線程同時只能有兩個被調度,用信號量實現
DWORD WINAPI ThreadEat(LPVOID p)
{
// 現在這樣寫應該有兩個線程永遠不執行
size_t id = (size_t)p;
// 信號量控制併發數
// 事件控制同步
while (szBuffer1[0] != 0 || szBuffer2[0] != 0 || szBufferRes[0] != 0)
{
Sleep(1000);
WaitForSingleObject(hSemaphore,INFINITE);
// 嘗試從1緩衝區取
DWORD dwRet = WaitForSingleObject(evtBuffer1Full, 0);
if (dwRet == WAIT_OBJECT_0)
{
GetWindowText(hEditEat[id], szBufferEat[id], 100);
strcat(szBufferEat[id], szBuffer1);
memset(szBuffer1, 0, 2);
SetWindowText(hEditBuffer1, szBuffer1);
SetWindowText(hEditEat[id], szBufferEat[id]);
SetEvent(evtBuffer1Empty);
}
// 嘗試從緩衝區2取
else
{
dwRet = WaitForSingleObject(evtBuffer2Full, 0);
if (dwRet == WAIT_OBJECT_0)
{
GetWindowText(hEditEat[id], szBufferEat[id], 100);
strcat(szBufferEat[id], szBuffer2);
memset(szBuffer2, 0, 2);
SetWindowText(hEditBuffer2, szBuffer2);
SetWindowText(hEditEat[id], szBufferEat[id]);
SetEvent(evtBuffer2Empty);
}
}
ReleaseSemaphore(hSemaphore, 1, NULL);
}
return 0;
}
DWORD WINAPI ThreadStart(LPVOID p)
{
// 必要的初始化工作
memset(szBufferRes, 0, 100);
memset(szBuffer1, 0, 2);
memset(szBuffer2, 0, 2);
GetWindowText(hEditRes, szBufferRes, 100);
// 初始化事件:緩衝區爲空
// 事件句柄應當在資源區爲空時關閉
evtBuffer1Empty = CreateEvent(0, FALSE, TRUE, NULL);
evtBuffer2Empty = CreateEvent(0, FALSE, TRUE, NULL);
evtBuffer1Full = CreateEvent(0, FALSE, FALSE, NULL);
evtBuffer2Full = CreateEvent(0, FALSE, FALSE, NULL);
// 創建1個資源線程
HANDLE hThreadCharToBuffer = CreateThread(0,0,ThreadCharToBuffer,0,0,0);
// 創建信號量控制併發數和4個喫貨線程
hSemaphore = CreateSemaphore(0,0,4,0); // 最大資源數4
HANDLE hThreadEat[4];
hThreadEat[0] = CreateThread(0,0,ThreadEat,(LPVOID)0,0,0);
hThreadEat[1] = CreateThread(0,0,ThreadEat,(LPVOID)1,0,0);
hThreadEat[2] = CreateThread(0,0,ThreadEat,(LPVOID)2,0,0);
hThreadEat[3] = CreateThread(0,0,ThreadEat,(LPVOID)3,0,0);
// 釋放兩個資源,這樣同時只能有2個線程被調度
ReleaseSemaphore(hSemaphore, 2, 0);
WaitForSingleObject(hThreadCharToBuffer, INFINITE);
WaitForMultipleObjects(4, hThreadEat, true, INFINITE);
CloseHandle(evtBuffer1Empty);
CloseHandle(evtBuffer2Empty);
MessageBox(0,"所有線程結束,所有事件關閉", "", MB_OK);
return 0;
}
BOOL CALLBACK MainProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
{
hEditRes = GetDlgItem(hDlg, IDC_EDIT_RES);
hEditBuffer1 = GetDlgItem(hDlg, IDC_EDIT_BUFFER1);
hEditBuffer2 = GetDlgItem(hDlg, IDC_EDIT_BUFFER2);
hEditEat[0] = GetDlgItem(hDlg, IDC_EDIT_A);
hEditEat[1] = GetDlgItem(hDlg, IDC_EDIT_B);
hEditEat[2] = GetDlgItem(hDlg, IDC_EDIT_C);
hEditEat[3] = GetDlgItem(hDlg, IDC_EDIT_D);
return TRUE;
}
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDC_BUTTON_START:
{
HANDLE hThread = CreateThread(0, 0, ThreadStart, 0, 0, 0);
CloseHandle(hThread);
return TRUE;
}
}
return TRUE;
}
case WM_CLOSE:
{
EndDialog(hDlg, 0);
return TRUE;
}
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG_MAIN), 0, MainProc);
return 0;
}