C++解决典型的IPC问题:哲学家就餐问题《C++多线程编程实战》

  • 互斥量:代表相互排斥或者能够给对象提供独占访问
  • 死锁:指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
  • 饥饿:进入死循环

解决方案有2个项目


  • Windows窗口项目
    pch.h
#ifndef PCH_H
#define PCH_H

// TODO: 添加要在此处预编译的标头
#pragma once
//#include "targetver.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <CommCtrl.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <stdio.h>
#pragma comment ( lib, "comctl32.lib" )
#pragma comment ( linker, "\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' \
version='6.0.0.0' processorArchitecture='*' \
publicKeyToken='6595b64144ccf1df' language='*'\"")

#endif //PCH_H

PhilosophersDinner.cpp

// PhilosophersDinner.cpp : 主应用程序

#include "pch.h"

#pragma warning(disable:4302)
#define BUTTON_CLOSE 100
#define PHILOSOPHER_COUNT 5
#define WM_INVALIDATE WM_USER + 1
#define _CRT_SECURE_NO_DEPRECATE
#define _SCL_SECURE_NO_DEPRECATE

typedef struct _tagCOMMUNICATIONOBJECT
{
	HWND hWnd;
	bool bExitApplication;
	int iPhilosopherArray[PHILOSOPHER_COUNT];
	int PhilosopherCount;
} COMMUNICATIONOBJECT, *PCOMMUNICATIONOBJECT;

HWND InitInstance(HINSTANCE hInstance, int nCmdShow);
ATOM MyRegisterClass(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
	LPARAM lParam);
int PhilosopherPass(int iPhilosopher);
void FillEllipse(HWND hWnd, HDC hDC, int iLeft, int iTop, int
	iRight, int iBottom, int iPass);

TCHAR* szTitle = (TCHAR*)TEXT("Philosophers Dinner Demo");
TCHAR* szWindowClass = (TCHAR*)TEXT("__PD_WND_CLASS__");
TCHAR* szSemaphoreName = (TCHAR*)TEXT("__PD_SEMAPHORE__");
TCHAR* szMappingName = (TCHAR*)TEXT("__SHARED_FILE_MAPPING__");
PCOMMUNICATIONOBJECT pCommObject = NULL;

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE
	hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);
	HANDLE hMapping = CreateFileMapping((HANDLE)-1, NULL,
		PAGE_READWRITE, 0, sizeof(COMMUNICATIONOBJECT), szMappingName);
	if (!hMapping)
	{
		MessageBox(NULL, TEXT("Cannot open file mapping"),
			TEXT("Error!"), MB_OK);
		return 1;
	}
	pCommObject = (PCOMMUNICATIONOBJECT)MapViewOfFile(hMapping,
		FILE_MAP_ALL_ACCESS, 0, 0, 0);
	if (!pCommObject)
	{
		MessageBox(NULL, TEXT("Cannot get access to file mapping! "),
			TEXT("Error!"), MB_OK);
		CloseHandle(hMapping);
		return 1;
	}
	InitCommonControls();
	MyRegisterClass(hInstance);
	HWND hWnd = NULL;
	if (!(hWnd = InitInstance(hInstance, nCmdShow)))
	{
		return FALSE;
	}
	pCommObject->bExitApplication = false;
	pCommObject->hWnd = hWnd;
	memset(pCommObject->iPhilosopherArray, 0,
		sizeof(*pCommObject->iPhilosopherArray));
	pCommObject->PhilosopherCount = PHILOSOPHER_COUNT;
	HANDLE hSemaphore = CreateSemaphore(NULL,
		int(PHILOSOPHER_COUNT / 2), int(PHILOSOPHER_COUNT / 2),
		szSemaphoreName);
	STARTUPINFO startupInfo[PHILOSOPHER_COUNT] =
		{ { 0 }, { 0 }, { 0 }, { 0 }, { 0 } };
	PROCESS_INFORMATION processInformation[PHILOSOPHER_COUNT] =
		{ { 0 }, { 0 }, { 0 }, { 0 }, { 0 } };
	HANDLE hProcesses[PHILOSOPHER_COUNT];
	TCHAR szBuffer[8];
	for (int iIndex = 0; iIndex < PHILOSOPHER_COUNT; iIndex++)
	{
#ifndef UNICODE
		wsprintf(szBuffer, L"%d", iIndex);
//#else
//		sprintf((char*)szBuffer, "%d", iIndex);
#endif // !UNICODE
		if (CreateProcess(TEXT("..\\Debug\\Philosopher.exe"),
			szBuffer, NULL, NULL,
			FALSE, 0, NULL, NULL, &startupInfo[iIndex],
			&processInformation[iIndex]))
		{
			hProcesses[iIndex] = processInformation[iIndex].hProcess;
		}
	}
	MSG msg = { 0 };
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	pCommObject->bExitApplication = true;
	UnmapViewOfFile(pCommObject);
	WaitForMultipleObjects(PHILOSOPHER_COUNT, hProcesses, TRUE, INFINITE);
	for (int iIndex = 0; iIndex < PHILOSOPHER_COUNT; iIndex++)
	{
		CloseHandle(hProcesses[iIndex]);
	}
	CloseHandle(hSemaphore);
	CloseHandle(hMapping);
	return (int)msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wndEx;
	wndEx.cbSize = sizeof(WNDCLASSEX);
	wndEx.style = CS_HREDRAW | CS_VREDRAW;
	wndEx.lpfnWndProc = WndProc;
	wndEx.cbClsExtra = 0;
	wndEx.cbWndExtra = 0;
	wndEx.hInstance = hInstance;
	wndEx.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
	wndEx.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndEx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wndEx.lpszClassName = szWindowClass;
	wndEx.lpszMenuName = NULL;
	wndEx.hIconSm = LoadIcon(wndEx.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
	return RegisterClassEx(&wndEx);
}
HWND InitInstance(HINSTANCE hInstance, int nCmdShow)
{
	HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPED
		| WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 200, 200, 540, 590,
		NULL, NULL, hInstance, NULL);
	if (!hWnd)
	{
		return NULL;
	}
	HFONT hFont = CreateFont(14, 0, 0, 0, FW_NORMAL, FALSE, FALSE,
		FALSE, BALTIC_CHARSET, OUT_DEFAULT_PRECIS,
		CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
		FF_MODERN, TEXT("Microsoft Sans Serif"));
	HWND hButton = CreateWindow(TEXT("BUTTON"), TEXT("Close"),
		WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP, 410, 520, 100,
		25, hWnd, (HMENU)BUTTON_CLOSE, hInstance, NULL);
	SendMessage(hButton, WM_SETFONT, (WPARAM)hFont, TRUE);
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);
	return hWnd;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
		case WM_COMMAND:
		{
			switch (LOWORD(wParam))
			{
				case BUTTON_CLOSE:
				{
					DestroyWindow(hWnd);
					break;
				}
			}
			break;
		}
		case WM_INVALIDATE:
		{
			InvalidateRect(hWnd, NULL, TRUE);
			break;
		}
		case WM_PAINT:
		{
			PAINTSTRUCT paintStruct;
			HDC hDC = BeginPaint(hWnd, &paintStruct);
			FillEllipse(hWnd, hDC, 210, 10, 310, 110,
				PhilosopherPass(1));
			FillEllipse(hWnd, hDC, 410, 170, 510, 270,
				PhilosopherPass(2));
			FillEllipse(hWnd, hDC, 335, 400, 435, 500,
				PhilosopherPass(3));
			FillEllipse(hWnd, hDC, 80, 400, 180, 500,
				PhilosopherPass(4));
			FillEllipse(hWnd, hDC, 10, 170, 110, 270,
				PhilosopherPass(5));
			EndPaint(hWnd, &paintStruct);
			break;
		}
		case WM_DESTROY:
		{
			PostQuitMessage(0);
			break;
		}
		default:
		{
			return DefWindowProc(hWnd, uMsg, wParam, lParam);
		}
	}
	return 0;
}
int PhilosopherPass(int iPhilosopher)
{
	return pCommObject->iPhilosopherArray[iPhilosopher - 1];
}
void FillEllipse(HWND hWnd, HDC hDC, int iLeft, int iTop, int
	iRight, int iBottom, int iPass)
{
	HBRUSH hBrush = NULL;
	if (iPass)
	{
		hBrush = CreateSolidBrush(RGB(255, 0, 0));
	}
	else
	{
		hBrush = CreateSolidBrush(RGB(255, 255, 255));
	}
	HBRUSH hOldBrush = (HBRUSH)SelectObject(hDC, hBrush);
	//Ellopse(hDC, iLeft, iTop, iRight, iBottom);
	SelectObject(hDC, hOldBrush);
	DeleteObject(hBrush);
}

  • 控制台应用程序项目
    pch.h
#ifndef PCH_H
#define PCH_H

// TODO: 添加要在此处预编译的标头
#pragma once
//#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <windows.h>

#endif //PCH_H

Philosopher.cpp

// Philosopher.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <Windows.h>

#define EATING_TIME 1000
#define PHILOSOPHER_COUNT 5
#define WM_INVALIDATE WM_USER + 1

typedef struct _tagCOMMUNICATIONOBJECT
{
	HWND hWnd;
	bool bExitApplication;
	int iPhilosopherArray[PHILOSOPHER_COUNT];
	int PhilosopherCount;
} COMMUNICATIONOBJECT, *PCOMMUNICATIONOBJECT;

void Eat();
TCHAR* szSemaphoreName = (TCHAR*)TEXT("__PD_SEMAPHORE__");
TCHAR* szMappingName = (TCHAR*)TEXT("__SHARED_FILE_MAPPING__");
bool bExitApplication = false;

int _tmain(int argc, _TCHAR* argv[])
{
	HWND hConsole = GetConsoleWindow();
	ShowWindow(hConsole, SW_HIDE);
	int iIndex = (int)_tcstol(argv[0], NULL, 10);
	HANDLE hMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE,
		szMappingName);
	while (!bExitApplication)
	{
		HANDLE hSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE,
			szSemaphoreName);
		WaitForSingleObject(hSemaphore, INFINITE);
		PCOMMUNICATIONOBJECT pCommObject = (PCOMMUNICATIONOBJECT)MapViewOfFile(hMapping,
			FILE_MAP_ALL_ACCESS, 0, 0, sizeof(COMMUNICATIONOBJECT));
		bExitApplication = pCommObject->bExitApplication;
		if (!pCommObject->iPhilosopherArray[
			(iIndex + pCommObject->PhilosopherCount - 1)
				% pCommObject->PhilosopherCount]
				&& !pCommObject->iPhilosopherArray[
					(iIndex + 1) % pCommObject->PhilosopherCount])
		{
			pCommObject->iPhilosopherArray[iIndex] = 1;
			Eat();
		}

		SendMessage(pCommObject->hWnd, WM_INVALIDATE, 0, 0);
		pCommObject->iPhilosopherArray[iIndex] = 0;
		UnmapViewOfFile(pCommObject);
		ReleaseSemaphore(hSemaphore, 1, NULL);
		CloseHandle(hSemaphore);
	}
	CloseHandle(hMapping);
	return 0;
}

void Eat()
{
	Sleep(EATING_TIME);
}


  • 运行结果
    在这里插入图片描述
哲学家就餐问题是一个经典的并发编程问题,用来展示多线程编程中的死锁和竞争条件等问题。下面是一个使用C++多线程解决哲学家就餐问题的示例代码: ```c++ #include <iostream> #include <thread> #include <mutex> using namespace std; const int kNumPhilosophers = 5; // 哲学家数量 mutex forks[kNumPhilosophers]; // 叉子锁 void philosopher(int id) { int left_fork = id; int right_fork = (id + 1) % kNumPhilosophers; // 模拟哲学家思考 cout << "Philosopher " << id << " is thinking." << endl; this_thread::sleep_for(chrono::seconds(1)); // 尝试获得叉子 cout << "Philosopher " << id << " is hungry and wants to eat." << endl; forks[left_fork].lock(); forks[right_fork].lock(); // 开始就餐 cout << "Philosopher " << id << " is eating." << endl; this_thread::sleep_for(chrono::seconds(1)); // 释放叉子 forks[right_fork].unlock(); forks[left_fork].unlock(); // 就餐结束 cout << "Philosopher " << id << " finished eating and is thinking again." << endl; } int main() { // 创建哲学家线程 thread philosophers[kNumPhilosophers]; for (int i = 0; i < kNumPhilosophers; i++) { philosophers[i] = thread(philosopher, i); } // 等待所有哲学家线程结束 for (int i = 0; i < kNumPhilosophers; i++) { philosophers[i].join(); } return 0; } ``` 在这个示例代码中,我们使用了`mutex`来实现叉子的锁机制,每个哲学家线程都会先尝试获得左边和右边的叉子,如果获得成功就开始就餐,否则就会等待。这个示例代码简单易懂,但是仍然存在死锁和竞争条件等问题,需要进一步的优化和改进。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值