互斥与同步,吃水果,吃巧克力问题
一、设计目的
了解、体会、运用、掌握操作系统互斥与同步。
二、问题描述
2.1问题描述
房间有一只碗,每次只能放入一个巧克力。丈夫专向碗中放黑巧克力,妻子专向碗中放白巧克力,小狗专等吃碗里的黑巧克力,小猫专等吃碗里的白巧克力。只要碗空,则丈夫或妻子都可以向碗放一个巧克力,仅当碗中有自己需要的巧克力时,小狗或小猫可以从碗中取出巧克力。
2.2实验要求
- 每次只能放进一块;
- 每次只能拿出一块;
- 放进和拿出不能同时发生;
- 在同一时间,只能有一种宠物吃一块;
- 在同一时间,只能有一个人放进一块;
- 丈夫放黑色的,妻子放白色的;
- 小狗只吃黑色的,小猫只吃白色的;
三、详细设计
3.1算法分析
丈夫和小狗是相互制约的,丈夫进程执行完,即往盘中放入黑巧克力后,小狗进程才能执行即吃黑巧克力,是同步关系;妻子和小猫是相互制约的,妻子进程执行完,即往盘中放入白巧克力,小猫进程才能执行即吃白巧克力,也是同步关系而丈夫和妻子这两个进程不能同时进行,是互斥关系。
3.2算法伪代码
/**********************伪代码**********************/
void father()
{
while(true)
{
wait(empty);//等待盘空,申请临界资源
produce black_chocolate;
signal(black_chocolate);//black_chocolate++,告知小狗有黑巧克力了
}
}
void mother()
{
while(true)
{
wait(empty);//等待盘空,申请临界资源
produce white_chocolate;
signal(white_chocolate);//white_chocolate++,告知小猫有白巧克力了
}
}
void dog()
{
while(true)
{
wait(black_chocolate);//等待黑巧克力
consume black_chocolate
signal(empty);//empty++,告知爸爸盘子空了
}
}
void cat()
{
while(true)
{
wait(white_chocolate);//等待白巧克力
consume white_chocolate
signal(empty);//empty++,告知妈妈盘子空了
}
}
3.3丈夫算法流程图
图3-2 父亲算法流程图
3.4妻子算法流程图
图3-3 母亲算法流程图
3.5小狗算法流程图
图3-3 小狗算法流程图
3.6小猫算法流程图
图3-3 小猫算法流程图
四、源代码(C++)
#include <windows.h>
#include <iostream>
using namespace std;
//声明句柄
HANDLE EmptyPlate; //声明空盘子
HANDLE husbandThread; //声明丈夫线程
HANDLE wifeThread; //声明妻子线程
HANDLE bCoco; //声明黑巧克力
HANDLE wCoco; //声明白巧克力
HANDLE dogThread; //声明小狗线程
HANDLE catThread; //声明小猫线程
//线程函数声明
DWORD WINAPI husband(LPVOID IpParameter); //丈夫线程函数声明
DWORD WINAPI wife(LPVOID IpParameter); //妻子线程函数声明
DWORD WINAPI dog(LPVOID IpParameter); //小狗线程函数声明
DWORD WINAPI cat(LPVOID IpParameter); //小猫线程函数声明
//初始化黑、白巧克力
int wConoNum = 0; //黑巧克力定为0
int bConoNum = 0; //白巧克力定为0
int num = 0;
class PvTest
{
public:
PvTest();
~PvTest();
};
PvTest::PvTest()
{
}
//析构回收线程以及信号量
PvTest::~PvTest()
{
}
int main()
{
cout << " 请输入线程的次数(>=0): " << endl;
cin >> num;
while (num < 0)
{
cout << " 输入线程的次数不符合要求,请重新输入! " << endl;
cout << " 请输入线程的次数(>=0): " << endl;
cin >> num;
}
//创建信号量
EmptyPlate = CreateSemaphore(NULL, 1, 1, NULL);
bCoco = CreateSemaphore(NULL, 0, 1, NULL); //黑巧克力
wCoco = CreateSemaphore(NULL, 0, 1, NULL); //白巧克力
//创建线程
// * 参数 1 内核安全属性 null 为默认
// * 参数 2 线程可用的空间大小
// * 参数 3 表示线程函数的地址
// * 参数 4 传给线程的参数
// * 参数 5 控制线程创建,为 0 表示创建后立即启动,CREATE_SUSPENDED 表示创建后先暂停
// * 参数 6 表示 lpTHreadId 返回的线程 ID , 传入 NULL 表示不需要 返回 ID 号
husbandThread = CreateThread(NULL, 0, husband, NULL, 0, NULL); //创建丈夫线程
wifeThread = CreateThread(NULL, 0, wife, NULL, 0, NULL); //创建妻子线程
dogThread = CreateThread(NULL, 0, dog, NULL, 0, NULL); //创建小狗线程
catThread = CreateThread(NULL, 0, cat, NULL, 0, NULL); //创建小猫线程
//等线程的结束
WaitForSingleObject(husbandThread, INFINITE); //等待丈夫线程结束
WaitForSingleObject(wifeThread, INFINITE); //等待妻子线程结束
WaitForSingleObject(dogThread, INFINITE); //等待小狗线程结束
WaitForSingleObject(catThread, INFINITE); //等待小猫线程结束
//关闭线程句柄
CloseHandle(husbandThread); //关闭丈夫线程
CloseHandle(wifeThread); //关闭妻子线程
CloseHandle(dogThread); //关闭小狗线程
CloseHandle(catThread); //关闭小猫线程
//关闭信号量句柄
CloseHandle(EmptyPlate); //关闭空盘子句柄
CloseHandle(wCoco); //关闭黑巧克力句柄
CloseHandle(bCoco); //关闭白巧克力句柄
return 0;
}
//丈夫线程
DWORD WINAPI husband(LPVOID IpParameter)
{
for (int i = 0; i < num; ++i)
{
WaitForSingleObject(EmptyPlate, INFINITE); //P操作
// 开始临界区
bConoNum++;
cout << "\n丈夫往盘中放一个黑巧克力\n";
cout << "盘子中情况:" << ((bConoNum == 1) ? "○" : "●") << endl;
// 结束临界区
ReleaseSemaphore(bCoco, 1, NULL); //V操作
Sleep(1000);
}
return 0;
}
//妻子线程
DWORD WINAPI wife(LPVOID IpParmeter)
{
for (int i = 0; i < num; ++i)
{
WaitForSingleObject(EmptyPlate, INFINITE); //P操作
// 开始临界区
wConoNum++;
cout << "\n妻子往盘中放一个白巧克\n";
cout << "盘子中情况:" << (( bConoNum == 1) ? "○" : "●") << endl;
// 结束临界区
ReleaseSemaphore(wCoco, 1, NULL); //V操作
Sleep(500);
}
return 0;
}
//小狗线程
DWORD WINAPI dog(LPVOID IpParameter)
{
for (int i = 0; i < num; ++i)
{
WaitForSingleObject(bCoco, INFINITE); //p操作
bConoNum--;
cout << "小狗吃黑巧克!" << endl;
ReleaseSemaphore(EmptyPlate, 1, NULL); //v操作
}
return 0;
}
//小猫线程
DWORD WINAPI cat(LPVOID IpParameter)
{
for (int i = 0; i < num; ++i)
{
WaitForSingleObject(wCoco, INFINITE); //p操作
wConoNum--;
cout << "小猫吃白巧克力!" << endl;
ReleaseSemaphore(EmptyPlate, 1, NULL); //v操作
}
return 0;
}