题目大意
有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;
}