继续完成操作原理实验三
博主选做完成的是第2,3,5题。
注:第2,3题在上一篇戳:操作系统原理实验三(一)
【当堂完成:[1,3,4]中任意 1 题和第2,5 题,共计 3 道题。】
(1)在 Ubuntu 或 Fedora 环境创建一对父子进程,使用共享内存的方式实现进程间的通信。父进程提供数据(1-100,递增),子进程读出来并显示。
(2)(考虑信号通信机制)在 Ubuntu 或 Fedora 环境创建父子 2 个进程 A,B。进程 A 不断获取用户从键盘输入的字符串或整数,通过信号机制传给进程 B。如果输入的是字符串,进程 B 将
其打印出来;如果输入的是整数,进程 B 将其累加起来,并输出该数和累加和。当累加和大于 100 时结束子进程,子进程输出“My work done!”后结束,然后父进程也结束。
(3)在 windows 环境使用创建一对父子进程,使用管道(pipe)的方式实现进程间的通信。父进程提供数据(1-100,递增),子进程读出来并显示。
(4)(考虑匿名管道通信)在 windows 环境下创建将 CMD 控制台程序封装为标准的 windows 窗口程序。
(5)在 windows 环境下,利用高级语言编程环境(限定为 VS 环境或 VC 环境或QT)调用 CreateThread 函数哲学家就餐问题的演示。要求:(1)提供死锁的解法和非死锁的解法;(2)有图形界面直观显示哲学家取筷子,吃饭,放筷子,思考等状态。(3)为增强结果的随机性,各个状态之间的维持时间采用随机时间,例如100ms-500ms 之间。
简述一下哲学家问题:
5名哲学家围桌而作,他们的循环进行着 思考-休息-吃饭 三个环节,
餐桌上只有5只筷子,必须同时拥有两只筷子才可以吃饭。
设哲学家先拿起左边筷子,再拿起右边筷子,然后吃饭。
于是就有可能发生,5名哲学家同时拿起左筷子,却又拿不到右筷子的情况,构成死锁条件。
另:给哲学家和筷子进行编号(哲学家与左手边筷子编号相同)
代码环境:VS2019+Easyx图形库(easyx图形库自行百度安装)
这里给出的是完整代码。完整代码有三部分:死锁情况、解法一、解法二
解法一、解法二仅仅是在死锁情况上做了稍许修改。
(代码开头有注释解释本解法的原理,仅修改了哲学家线程,可以只看这一部分)
死锁情况:
#include
#include
#include
#include
#include
#define P(S) WaitForSingleObject(S,INFINITE); //定义Windows下的P操作
#define V(S) ReleaseSemaphore(S,1,NULL) //定义Windows下的V操作
//预设5个哲学家 5只筷子
#define num_of_philosopher 5
#define num_of_chopsticks 5
//定义互斥量 分别代表5只筷子
HANDLE S[5];
//记录每个哲学家的状态 0->思考 1->休息 2->取得了左边筷子 3->两只筷子都获得,在吃饭
//记录了状态是为了做图形界面时可以直接读取到每个哲学家的状态
int status[5] = {
0 };
//定义线程 哲学家
//参数 pM-->哲学家编号
DWORD WINAPI philosopher(LPVOID pM)
{
int id = *((int*)(pM));//获得哲学家编号
//printf("%d号哲学家\n",id);
//Sleep(10);
while (1)
{
//他们思考、休息、吃饭的时间由随机数决定(100~500ms)
int time_of_thinking = rand() % 401 + 100;
int time_of_resting = rand() % 401 + 100;
int time_of_eating = rand() % 401 + 100;
//哲学家处于思考状态
status[id] = 0;
// printf("%d号哲学家正在思考\n", id);
Sleep(time_of_thinking);
//哲学家处于休息状态
status[id] = 1;
// printf("%d号哲学家正在休息\n", id);
Sleep(time_of_resting);
//哲学家处于吃饭状态
P(S[id]); //取左边筷子
status[id] = 2;
//printf("%d号哲学家取得了左边筷子\n", id);
P(S[(id + 4) % 5]); //取右边筷子
status[id] = 3;
// printf("%d号哲学家取得了右边筷子,正在吃饭\n", id);
Sleep(time_of_eating);
V(S[(id + 4) % 5]); //放下右边筷子
V(S[id]); //放下左边筷子
//一次流程到这里就结束了
}
}
//绘图相关
IMAGE desk_img, chopstick_0, chopstick_1, chopstick_2, chopstick_3, chopstick_4;
IMAGE thinking_imgs[5],resting_imgs[5],left_imgs[5],eating_imgs[5];
void load_image()
{
loadimage(&desk_img, _T("./圆桌.jpg"), 600, 600);
loadimage(&chopstick_0, _T("./筷子_0.png"),60, 60);
loadimage(&chopstick_1, _T("./筷子_1.png"), 60, 60);
loadimage(&chopstick_2, _T("./筷子_2.png"), 60, 60);
loadimage(&chopstick_3, _T("./筷子_3.png"), 60, 60);
loadimage(&chopstick_4, _T("./筷子_4.png"), 60, 60);
loadimage(&thinking_imgs[0], _T("./思考_0.png"), 137, 64);
loadimage(&thinking_imgs[1], _T("./思考_1.png"), 137, 64);
loadimage(&thinking_imgs[2], _T("./思考_2.png"), 137, 64);
loadimage(&thinking_imgs[3], _T("./思考_3.png"), 137, 64);
loadimage(&thinking_imgs[4], _T("./思考_4.png"), 137, 64);
loadimage(&resting_imgs[0], _T("./休息_0.png"), 137, 64);
loadimage(&resting_imgs[1], _T("./休息_1.png"), 137, 64);
loadimage(&resting_imgs[2], _T("./休息_2.png"), 137, 64);
loadimage(&resting_imgs[3], _T("./休息_3.png"), 137, 64);
loadimage(&resting_imgs[4], _T("./休息_4.png"), 137, 64);
loadimage(&left_imgs[0], _T("./左筷_0.png"), 137, 64);
loadimage(&left_imgs[1], _T("./左筷_1.png"), 137, 64);
loadimage(&left_imgs[2], _T("./左筷_2.png"), 137, 64);
loadimage(&left_imgs[3], _T("./左筷_3.png"), 137, 64);
loadimage(&left_imgs[4], _T("./左筷_4.png"), 137, 64);
loadimage(&eating_imgs[0], _T("./吃饭_0.png"), 137, 64);
loadimage(&eating_imgs[1], _T("./吃饭_1.png"), 137, 64);
loadimage(&eating_imgs[2], _T("./吃饭_2.png"), 137, 64);
loadimage(&eating_imgs[3], _T("./吃饭_3.png"), 137, 64);
loadimage(&eating_imgs[4], _T("./吃饭_4.png"), 137, 64);
}
// 载入PNG图并去透明部分
void drawAlpha(int picture_x, int picture_y, IMAGE* picture) //x为载入图片的X坐标,y为Y坐标
{
// 变量初始化
DWORD* dst = GetImageBuffer(); // GetImageBuffer()函数,用于获取绘图设备的显存指针,EASYX自带
DWORD* draw = GetImageBuffer();
DWORD* src = GetImageBuffer(picture); //获取picture的显存指针
int picture_width = picture->getwidth(); //获取picture的宽度,EASYX自带
int picture_height = picture->getheight(); //获取picture的高度,EASYX自带
int graphWidth = getwidth(); //获取绘图区的宽度,EASYX自带
int graphHeight = getheight(); //获取绘图区的高度,EASYX自带
int dstX = 0; //在显存里像素的角标
// 实现透明贴图 公式: Cp=αp*FP+(1-αp)*BP , 贝叶斯定理来进行点颜色的概率计算
for (int iy = 0; iy < picture_height; iy++)
{
for (int ix = 0; ix < picture_width; ix++)
{
int srcX = ix + iy * picture_width; //在显存里像素的角标
int sa = ((src[srcX] & 0xff000000) >> 24); //0xAArrggbb;AA是透明度
int sr = ((src[srcX] & 0xff0000) >> 16); //获取RGB里的R
int sg = ((src[srcX] & 0xff00) >> 8); //G
int sb = src[srcX] & 0xff; //B
if (ix >= 0 && ix <= graphWidth && iy >= 0 && iy <= graphHeight && dstX <= graphWidth * graphHeight)
{
dstX = (ix + picture_x) + (iy + picture_y) * graphWidth; //在显存里像素的角标
int dr = ((dst[dstX] & 0xff0000) >> 16);
int dg = ((dst[dstX] & 0xff00) >> 8);
int db = dst[dstX] & 0xff;
draw[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) << 16) //公式: Cp=αp*FP+(1-αp)*BP ; αp=sa/255 , FP=sr , BP=dr
| ((sg * sa / 255 + dg * (255 - sa) / 255) << 8) //αp=sa/255 , FP=sg , BP=dg
| (sb * sa / 255 + db * (255 - sa) / 255); //αp=sa/255 , FP=sb , BP=db
}
}
}
}
void draw()
{
cleardevice();
BeginBatchDraw();
setbkcolor(RGB(255, 255, 255));
cleardevice();//用白色清空背景板
//画桌子
putimage(0, 50 , &desk_img);
//画筷子
if (status[0] <= 1 && status[1] <= 1)
drawAlpha(325, 252, &chopstick_0);
if (status[1] <= 1 && status[2] <= 1)
drawAlpha(362, 397, &chopstick_1);
if (status[2] <= 1 && status[3] <= 1)
drawAlpha(270, 447, &chopstick_2);
if (status[3] <= 1 && status[4] <= 1)
drawAlpha(178, 397, &chopstick_3);
if (status[4] <= 1 && status[0] <= 1)
drawAlpha(208, 258, &chopstick_4);
//画状态
for (int id = 0; id < num_of_philosopher; id++)
{
int X, Y;
switch (id)
{
case 0:
X = 232;
Y = 0;
break;
case 1:
X = 450;
Y = 150;
break;
case 2:
X = 370;
Y = 636;
break;
case 3:
X = 100;
Y = 636;
break;
case 4:
X = 0;
Y = 150;
break;
}
if(status[id]==0)
drawAlpha(X, Y, &thinking_imgs[id]);
else if(status[id] == 1)
drawAlpha(X, Y, &resting_imgs[id]);
else if (status[id] == 2)
drawAlpha(X, Y, &left_imgs[id]);
else if (status[id] == 3)
drawAlpha(X, Y, &eating_imgs[id]);
}
EndBatchDraw();
}
int main()
{
srand((unsigned)time(NULL));
//创建互斥量
for(int i=0;i<num_of_chopsticks;i++)
S[i] = CreateSemaphore(NULL, 1, 1, NULL);
//创建线程
HANDLE hThread[num_of_philosopher];
int pID[5];
for (int i = 0; i < num_of_philosopher; i++)
{
pID[i] = i ;//为什么要设置一个数组,因为传参传的是指针
//参数: (LPVOID*)&pID[i]是哲学家编号
if ((hThread[i] = CreateThread(NULL, 0, philosopher, (LPVOID*)&pID[i], 0, NULL)) == NULL)
{
printf("Create %d philosopher_thread failed...",i);
exit(1);
}
}
//画图
initgraph(600, 700);
load_image();
while (1)
{
//如果不在画图前暂停线程的话,那么在画图过程中线程的状态就可能改变,而使得在图上表现出冲突
SuspendThread(hThread);//暂停线程
draw();
ResumeThread(hThread);//继续线程
}
//等待线程退出
WaitForMultipleObjects(num_of_philosopher, hThread, TRUE, INFINITE);
}
解法一:
/*
解法一:限制最多有4个人同时去拿左边的筷子,
那么一定有一个哲学家可以完成吃饭,他放下筷子后其他哲学家可以继续进行吃饭
因此不会形成死锁
实现方法:设置信号量 full 初值为4 哲学家拿左筷之前执行P(full)
*/
#include
#include
#include
#include
#include
#define P(S) WaitForSingleObject(S,INFINITE); //定义Windows下的P操作
#define V(S) ReleaseSemaphore(S,1,NULL) //定义Windows下的V操作
//预设5个哲学家 5只筷子 最多4个同时拿起左筷
#define num_of_philosopher 5
#define num_of_chopsticks 5
#define max_num 4
//定义互斥量 分别代表5只筷子
HANDLE S[5];
//定义信号量 初值为4 代表最大拿起左筷人数
HANDLE full;
//记录每个哲学家的状态 0->思考 1->休息 2->取得了左边筷子 3->两只筷子都获得,在吃饭
//记录了状态是为了做图形界面时可以直接读取到每个哲学家的状态
int status[5] = {
0 };
//定义线程 哲学家
//参数 pM-->哲学家编号
DWORD WINAPI philosopher(LPVOID pM)
{
int id = *((int*)(pM));//获得哲学家编号
//printf("%d号哲学家\n",id);
//Sleep(10);
while (1)
{
//他们思考、休息、吃饭的时间由随机数决定(100~500ms)
int time_of_thinking = rand() % 401 + 100;
int time_of_resting = rand() % 401 + 100;
int time_of_eating = rand() % 401 + 100;
//哲学家处于思考状态
status[id] = 0;
// printf("%d号哲学家正在思考\n", id);
Sleep(time_of_thinking);
//哲学家处于休息状态
status[id] = 1;
// printf("%d号哲学家正在休息\n", id);
Sleep(time_of_resting);
//哲学家处于吃饭状态
P(full); //最多4个人同时拿起左筷
P(S[id]); //取左边筷子
status[id] = 2;
//printf("%d号哲学家取得了左边筷子\n", id);
P(S[(id + 4) % 5]); //取右边筷子
status[id] = 3;
// printf("%d号哲学家取得了右边筷子,正在吃饭\n", id);
Sleep(time_of_eating);
V(S[(id + 4) % 5]); //放下右边筷子
V(S[id]); //放下左边筷子
V(full);
//一次流程到这里就结束了
}
}
IMAGE desk_img, chopstick_0, chopstick_1, chopstick_2, chopstick_3, chopstick_4;
IMAGE thinking_imgs[5], resting_imgs[5], left_imgs[5], eating_imgs[5];
void load_image()
{
loadimage(&desk_img, _T("./圆桌.jpg"), 600, 600);
loadimage(&chopstick_0, _T("./筷子_0.png"), 60, 60);
loadimage(&chopstick_1, _T("./筷子_1.png"), 60, 60);
loadimage(&chopstick_2, _T("./筷子_2.png"), 60, 60);
loadimage(&chopstick_3, _T("./筷子_3.png"), 60, 60);
loadimage(&chopstick_4, _T("./筷子_4.png"), 60, 60);
loadimage(&thinking_imgs[0], _T("./思考_0.png"), 137, 64);
loadimage(&thinking_imgs[1], _T("./思考_1.png"), 137, 64);
loadimage(&thinking_imgs[2], _T("./思考_2.png"), 137, 64);
loadimage(&thinking_imgs[3], _T("./思考_3.png"), 137, 64);
loadimage(&thinking_imgs[4], _T("./思考_4.png"), 137, 64);
loadimage(&resting_imgs[0], _T("./休息_0.png"), 137, 64);
loadimage(&resting_imgs[1], _T("./休息_1.png"), 137, 64);
loadimage(&resting_imgs[2], _T("./休息_2.png"), 137, 64);
loadimage(&resting_imgs[3], _T("./休息_3.png"), 137, 64);
loadimage(&resting_imgs[4], _T("./休息_4.png"), 137, 64);
loadimage(&left_imgs[0], _T("./左筷_0.png"), 137, 64);
loadimage(&left_imgs[1], _T("./左筷_1.png"), 137, 64);
loadimage(&left_imgs[2], _T("./左筷_2.png"), 137, 64);
loadimage(&left_imgs[3], _T("./左筷_3.png"), 137, 64);
loadimage(&left_imgs[4], _T("./左筷_4.png"), 137, 64);
loadimage(&eating_imgs[0], _T("./吃饭_0.png"), 137, 64);
loadimage(&eating_imgs[1], _T("./吃饭_1.png"), 137, 64);
loadimage(&eating_imgs[2], _T("./吃饭_2.png"), 137, 64);
loadimage(&eating_imgs[3], _T("./吃饭_3.png"), 137, 64);
loadimage(&eating_imgs[4], _T("./吃饭_4.png"), 137, 64);
}
// 载入PNG图并去透明部分
void drawAlpha(int picture_x, int picture_y, IMAGE* picture) //x为载入图片的X坐标,y为Y坐标
{
// 变量初始化
DWORD* dst = GetImageBuffer(); // GetImageBuffer()函数,用于获取绘图设备的显存指针,EASYX自带
DWORD* draw = GetImageBuffer();
DWORD* src = GetImageBuffer(picture); //获取picture的显存指针
int picture_width = picture->getwidth(); //获取picture的宽度,EASYX自带
int picture_height = picture->getheight(); //获取picture的高度,EASYX自带
int graphWidth = getwidth(); //获取绘图区的宽度,EASYX自带
int graphHeight = getheight(); //获取绘图区的高度,EASYX自带
int dstX = 0; //在显存里像素的角标
// 实现透明贴图 公式: Cp=αp*FP+(1-αp)*BP , 贝叶斯定理来进行点颜色的概率计算
for (int iy = 0; iy < picture_height; iy++)
{
for (int ix = 0; ix < picture_width; ix++)
{
int srcX = ix + iy * picture_width; //在显存里像素的角标
int sa = ((src[srcX] & 0xff000000) >> 24); //0xAArrggbb;AA是透明度
int sr = ((src[srcX] & 0xff0000) >> 16); //获取RGB里的R
int sg = ((src[srcX] & 0xff00) >> 8); //G
int sb = src[srcX] & 0xff; //B
if (ix >= 0 && ix <= graphWidth && iy >= 0 && iy <= graphHeight && dstX <= graphWidth * graphHeight)
{
dstX = (ix + picture_x) + (iy + picture_y) * graphWidth; //在显存里像素的角标
int dr = ((dst[dstX] & 0xff0000) >> 16);
int dg = ((dst[dstX] & 0xff00) >> 8);
int db = dst[dstX] & 0xff;
draw[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) << 16) //公式: Cp=αp*FP+(1-αp)*BP ; αp=sa/255 , FP=sr , BP=dr
| ((sg * sa / 255 + dg * (255 - sa) / 255) << 8) //αp=sa/255 , FP=sg , BP=dg
| (sb * sa / 255 + db * (255 - sa) / 255); //αp=sa/255 , FP=sb , BP=db
}
}
}
}
void draw()
{
cleardevice();
BeginBatchDraw();
setbkcolor(RGB(255, 255, 255));
cleardevice();//用白色清空背景板
//画桌子
putimage(0, 50, &desk_img);
//画筷子
if (status[0] <= 1 && status[1] <= 1)
drawAlpha(325, 252, &chopstick_0);
if (status[1] <= 1 && status[2] <= 1)
drawAlpha(362, 397, &chopstick_1);
if (status[2] <= 1 && status[3] <= 1)
drawAlpha(270, 447, &chopstick_2);
if (status[3] <= 1 && status[4] <= 1)
drawAlpha(178, 397, &chopstick_3);
if (status[4] <= 1 && status[0] <= 1)
drawAlpha(208, 258, &chopstick_4);
//画状态
for (int id = 0; id < num_of_philosopher; id++)
{
int X, Y;
switch (id)
{
case 0:
X = 232;
Y = 0;
break;
case 1:
X = 450;
Y = 150;
break;
case 2:
X = 370;
Y = 636;
break;
case 3:
X = 100;
Y = 636;
break;
case 4:
X = 0;
Y = 150;
break;
}
if (status[id] == 0)
drawAlpha(X, Y, &thinking_imgs[id]);
else if (status[id] == 1)
drawAlpha(X, Y, &resting_imgs[id]);
else if (status[id] == 2)
drawAlpha(X, Y, &left_imgs[id]);
else if (status[id] == 3)
drawAlpha(X, Y, &eating_imgs[id]);
}
EndBatchDraw();
}
int main()
{
srand((unsigned)time(NULL));
//创建互斥量
for (int i = 0; i < num_of_chopsticks; i++)
S[i] = CreateSemaphore(NULL, 1, 1, NULL);
//创建信号量 初值为max_num
//参数分别表示 继承/初始化信号值/最大信号值/名称
full= CreateSemaphore(NULL, max_num, max_num, NULL);
//创建线程
HANDLE hThread[num_of_philosopher];
int pID[5];
for (int i = 0; i < num_of_philosopher; i++)
{
pID[i] = i;//为什么要设置一个数组,因为传参传的是指针
//参数: (LPVOID*)&pID[i]是哲学家编号
if ((hThread[i] = CreateThread(NULL, 0, philosopher, (LPVOID*)&pID[i], 0, NULL)) == NULL)
{
printf("Create %d philosopher_thread failed...", i);
exit(1);
}
}
//画图
initgraph(600, 700);
load_image();
while (1)
{
//如果不在画图前暂停线程的话,那么在画图过程中线程的状态就可能改变,而使得在图上表现出冲突
SuspendThread(hThread);//暂停线程
draw();
ResumeThread(hThread);//继续线程
}
//等待线程退出
WaitForMultipleObjects(num_of_philosopher, hThread, TRUE, INFINITE);
}
解法二:
/*
解法二:当哲学家左右两边的筷子都可用时才允许他去拿筷子,
因此不会形成死锁
实现方法:拿起左筷前 检测相邻两位哲学家的状态(<=1,才可以拿)
缺点:无法设立一个信号量来表示左右两侧哲学家的状态,因此实现时只能在一个while(1)循环里不断地检测if(<=1)条件
这可能会造成CPU资源的浪费
*/
#include
#include
#include
#include
#include
#define P(S) WaitForSingleObject(S,INFINITE); //定义Windows下的P操作
#define V(S) ReleaseSemaphore(S,1,NULL) //定义Windows下的V操作
//预设5个哲学家 5只筷子
#define num_of_philosopher 5
#define num_of_chopsticks 5
//定义互斥量 分别代表5只筷子
HANDLE S[5];
//记录每个哲学家的状态 0->思考 1->休息 2->取得了左边筷子 3->两只筷子都获得,在吃饭
//记录了状态是为了做图形界面时可以直接读取到每个哲学家的状态
int status[5] = {
0 };
//定义线程 哲学家
//参数 pM-->哲学家编号
DWORD WINAPI philosopher(LPVOID pM)
{
int id = *((int*)(pM));//获得哲学家编号
//printf("%d号哲学家\n",id);
//Sleep(10);
while (1)
{
//他们思考、休息、吃饭的时间由随机数决定(100~500ms)
int time_of_thinking = rand() % 401 + 100;
int time_of_resting = rand() % 401 + 100;
int time_of_eating = rand() % 401 + 100;
//哲学家处于思考状态
status[id] = 0;
// printf("%d号哲学家正在思考\n", id);
Sleep(time_of_thinking);
//哲学家处于休息状态
status[id] = 1;
// printf("%d号哲学家正在休息\n", id);
Sleep(time_of_resting);
//哲学家处于吃饭状态
while (1)
{
//若左右两边的哲学家都没有拿起筷子,这个哲学家才可以去拿筷子
if (status[(id + 1) % 5] <= 1 && status[(id + 4) % 5] <= 1)
break;
}
P(S[id]); //取左边筷子
status[id] = 2;
//printf("%d号哲学家取得了左边筷子\n", id);
P(S[(id + 4) % 5]); //取右边筷子
status[id] = 3;
// printf("%d号哲学家取得了右边筷子,正在吃饭\n", id);
Sleep(time_of_eating);
V(S[(id + 4) % 5]); //放下右边筷子
V(S[id]); //放下左边筷子
//一次流程到这里就结束了
}
}
IMAGE desk_img, chopstick_0, chopstick_1, chopstick_2, chopstick_3, chopstick_4;
IMAGE thinking_imgs[5],resting_imgs[5],left_imgs[5],eating_imgs[5];
void load_image()
{
loadimage(&desk_img, _T("./圆桌.jpg"), 600, 600);
loadimage(&chopstick_0, _T("./筷子_0.png"),60, 60);
loadimage(&chopstick_1, _T("./筷子_1.png"), 60, 60);
loadimage(&chopstick_2, _T("./筷子_2.png"), 60, 60);
loadimage(&chopstick_3, _T("./筷子_3.png"), 60, 60);
loadimage(&chopstick_4, _T("./筷子_4.png"), 60, 60);
loadimage(&thinking_imgs[0], _T("./思考_0.png"), 137, 64);
loadimage(&thinking_imgs[1], _T("./思考_1.png"), 137, 64);
loadimage(&thinking_imgs[2], _T("./思考_2.png"), 137, 64);
loadimage(&thinking_imgs[3], _T("./思考_3.png"), 137, 64);
loadimage(&thinking_imgs[4], _T("./思考_4.png"), 137, 64);
loadimage(&resting_imgs[0], _T("./休息_0.png"), 137, 64);
loadimage(&resting_imgs[1], _T("./休息_1.png"), 137, 64);
loadimage(&resting_imgs[2], _T("./休息_2.png"), 137, 64);
loadimage(&resting_imgs[3], _T("./休息_3.png"), 137, 64);
loadimage(&resting_imgs[4], _T("./休息_4.png"), 137, 64);
loadimage(&left_imgs[0], _T("./左筷_0.png"), 137, 64);
loadimage(&left_imgs[1], _T("./左筷_1.png"), 137, 64);
loadimage(&left_imgs[2], _T("./左筷_2.png"), 137, 64);
loadimage(&left_imgs[3], _T("./左筷_3.png"), 137, 64);
loadimage(&left_imgs[4], _T("./左筷_4.png"), 137, 64);
loadimage(&eating_imgs[0], _T("./吃饭_0.png"), 137, 64);
loadimage(&eating_imgs[1], _T("./吃饭_1.png"), 137, 64);
loadimage(&eating_imgs[2], _T("./吃饭_2.png"), 137, 64);
loadimage(&eating_imgs[3], _T("./吃饭_3.png"), 137, 64);
loadimage(&eating_imgs[4], _T("./吃饭_4.png"), 137, 64);
}
// 载入PNG图并去透明部分
void drawAlpha(int picture_x, int picture_y, IMAGE* picture) //x为载入图片的X坐标,y为Y坐标
{
// 变量初始化
DWORD* dst = GetImageBuffer(); // GetImageBuffer()函数,用于获取绘图设备的显存指针,EASYX自带
DWORD* draw = GetImageBuffer();
DWORD* src = GetImageBuffer(picture); //获取picture的显存指针
int picture_width = picture->getwidth(); //获取picture的宽度,EASYX自带
int picture_height = picture->getheight(); //获取picture的高度,EASYX自带
int graphWidth = getwidth(); //获取绘图区的宽度,EASYX自带
int graphHeight = getheight(); //获取绘图区的高度,EASYX自带
int dstX = 0; //在显存里像素的角标
// 实现透明贴图 公式: Cp=αp*FP+(1-αp)*BP , 贝叶斯定理来进行点颜色的概率计算
for (int iy = 0; iy < picture_height; iy++)
{
for (int ix = 0; ix < picture_width; ix++)
{
int srcX = ix + iy * picture_width; //在显存里像素的角标
int sa = ((src[srcX] & 0xff000000) >> 24); //0xAArrggbb;AA是透明度
int sr = ((src[srcX] & 0xff0000) >> 16); //获取RGB里的R
int sg = ((src[srcX] & 0xff00) >> 8); //G
int sb = src[srcX] & 0xff; //B
if (ix >= 0 && ix <= graphWidth && iy >= 0 && iy <= graphHeight && dstX <= graphWidth * graphHeight)
{
dstX = (ix + picture_x) + (iy + picture_y) * graphWidth; //在显存里像素的角标
int dr = ((dst[dstX] & 0xff0000) >> 16);
int dg = ((dst[dstX] & 0xff00) >> 8);
int db = dst[dstX] & 0xff;
draw[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) << 16) //公式: Cp=αp*FP+(1-αp)*BP ; αp=sa/255 , FP=sr , BP=dr
| ((sg * sa / 255 + dg * (255 - sa) / 255) << 8) //αp=sa/255 , FP=sg , BP=dg
| (sb * sa / 255 + db * (255 - sa) / 255); //αp=sa/255 , FP=sb , BP=db
}
}
}
}
void draw()
{
cleardevice();
BeginBatchDraw();
setbkcolor(RGB(255, 255, 255));
cleardevice();//用白色清空背景板
//画桌子
putimage(0, 50 , &desk_img);
//画筷子
if (status[0] <= 1 && status[1] <= 1)
drawAlpha(325, 252, &chopstick_0);
if (status[1] <= 1 && status[2] <= 1)
drawAlpha(362, 397, &chopstick_1);
if (status[2] <= 1 && status[3] <= 1)
drawAlpha(270, 447, &chopstick_2);
if (status[3] <= 1 && status[4] <= 1)
drawAlpha(178, 397, &chopstick_3);
if (status[4] <= 1 && status[0] <= 1)
drawAlpha(208, 258, &chopstick_4);
//画状态
for (int id = 0; id < num_of_philosopher; id++)
{
int X, Y;
switch (id)
{
case 0:
X = 232;
Y = 0;
break;
case 1:
X = 450;
Y = 150;
break;
case 2:
X = 370;
Y = 636;
break;
case 3:
X = 100;
Y = 636;
break;
case 4:
X = 0;
Y = 150;
break;
}
if(status[id]==0)
drawAlpha(X, Y, &thinking_imgs[id]);
else if(status[id] == 1)
drawAlpha(X, Y, &resting_imgs[id]);
else if (status[id] == 2)
drawAlpha(X, Y, &left_imgs[id]);
else if (status[id] == 3)
drawAlpha(X, Y, &eating_imgs[id]);
}
EndBatchDraw();
}
int main()
{
srand((unsigned)time(NULL));
//创建互斥量
for(int i=0;i<num_of_chopsticks;i++)
S[i] = CreateSemaphore(NULL, 1, 1, NULL);
//创建线程
HANDLE hThread[num_of_philosopher];
int pID[5];
for (int i = 0; i < num_of_philosopher; i++)
{
pID[i] = i ;//为什么要设置一个数组,因为传参传的是指针
//参数: (LPVOID*)&pID[i]是哲学家编号
if ((hThread[i] = CreateThread(NULL, 0, philosopher, (LPVOID*)&pID[i], 0, NULL)) == NULL)
{
printf("Create %d philosopher_thread failed...",i);
exit(1);
}
}
//画图
initgraph(600, 700);
load_image();
while (1)
{
//如果不在画图前暂停线程的话,那么在画图过程中线程的状态就可能改变,而使得在图上表现出冲突
SuspendThread(hThread);//暂停线程
draw();
ResumeThread(hThread);//继续线程
}
//等待线程退出
WaitForMultipleObjects(num_of_philosopher, hThread, TRUE, INFINITE);
}
1.创建线程:
函数原型:
HANDLE WINAPI CreateThread(
In_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
In SIZE_T dwStackSize,
In LPTHREAD_START_ROUTINE lpStartAddress,
In_opt LPVOID lpParameter,
In DWORD dwCreationFlags,
Out_opt LPDWORD lpThreadId
);
- 参数说明:
第一个参数 lpThreadAttributes 表示线程内核对象的安全属性,一般传入NULL表示使用默认设置。
第二个参数 dwStackSize 表示线程栈空间大小。传入0表示使用默认大小(1MB)。
第三个参数 lpStartAddress 表示新线程所执行的线程函数地址,多个线程可以使用同一个函数地址。
第四个参数 lpParameter 是传给线程函数的参数。
第五个参数 dwCreationFlags 指定额外的标志来控制线程的创建,为0表示线程创建之后立即就可以进行调度,如果为CREATE_SUSPENDED则表示线程创建后暂停运行,这样它就无法调度,直到调用ResumeThread()。
第六个参数 lpThreadId 将返回线程的ID号,传入NULL表示不需要返回该线程ID号。
3.返回值 线程创建成功返回新线程的句柄,失败返回NULL
我只是理论的搬运工hhhh
附参考链接:windows多线程(一) 创建线程 CreateThread
2.哲学家线程
DWORD WINAPI philosopher(LPVOID pM)
{
int id = *((int*)(pM));//获得哲学家编号
while(1)
{
首先是获取哲学家编号id(0~4)
//他们思考、休息、吃饭的时间由随机数决定(100~500ms)
int time_of_thinking = rand() % 401 + 100;
int time_of_resting = rand() % 401 + 100;
int time_of_eating = rand() % 401 + 100;
然后通过随机数确定他们思考、休息、吃饭消耗的时间(100~500ms)
//哲学家处于思考状态
status[id] = 0;
// printf("%d号哲学家正在思考\n", id);
Sleep(time_of_thinking);
//哲学家处于休息状态
status[id] = 1;
// printf("%d号哲学家正在休息\n", id);
Sleep(time_of_resting);
哲学家处于思考态时status[id]=0
处于休息态时设置status[id]=1
//哲学家处于吃饭状态
P(S[id]); //取左边筷子
status[id] = 2;
//printf("%d号哲学家取得了左边筷子\n", id);
P(S[(id + 4) % 5]); //取右边筷子
status[id] = 3;
// printf("%d号哲学家取得了右边筷子,正在吃饭\n", id);
Sleep(time_of_eating);
V(S[(id + 4) % 5]); //放下右边筷子
V(S[id]); //放下左边筷子
//一次流程到这里就结束了
}
}
哲学家吃饭前要先拿起左边筷子此时status[id]=2
再拿起右边筷子并吃饭,status[id]=3
注意拿筷子改变状态前需要P(S[id])(筷子有一个编号,哲学家与左手边筷子编号相同。)
3.画图
使用了Easyx图形库,大家可以自行百度安装一下,挺方便的。
loadimage()是将图片加载进来
putimage()是将图片贴到背景上去
drawAlpha()是自定义的函数,因为putimage()不能处理透明的图片,所以在网上找到一个大佬的代码,可以实现有透明背景的图片粘贴(筷子的背景是透明的)
图片都是我自己P的,素材来源:感谢阿里爸爸的iconfont图形库
我会在最后把代码及图片资源传上去。
4.解法一
解法一:限制最多有4个人同时去拿左边的筷子,
那么一定有一个哲学家可以完成吃饭,他放下筷子后其他哲学家可以继续进行吃饭
因此不会形成死锁
实现方法:设置信号量 full 初值为4 哲学家拿左筷之前执行P(full),吃完饭后执行V(full)
5.解法二
解法二:当哲学家左右两边的筷子都可用时才允许他去拿筷子,因此不会形成死锁
实现方法:拿起左筷前 检测相邻两位哲学家的状态(<=1,才可以拿)
缺点:无法设立一个信号量来表示左右两侧哲学家的状态,因此实现时只能在一个while(1)循环里不断地检测if(<=1)条件
这可能会造成CPU资源的浪费
链接:https://download.csdn.net/download/zsh1184528359/12393157
木得积分可以评论区留下邮箱,我看到了就会发的(●’◡’●)
点击下图画框框的《哲学家就餐.exe》就可以直接运行
另外:代码中解法一、解法二全篇注释中(因为一次只能运行一个main,所以三个.c只能有一个没有注释)
Ctrl+A 全选,然后Ctrl+K+U解除注释
Ctrl+A 全选,然后Ctrl+K+C全篇注释
18级软院的哥哥姐姐们,如果用到的话,建议图自己P一下,搞的都一样就不好了(>人<;)
希望以上可以帮到你!
如有错误,或不同想法,欢迎指出,互相学习共同进步!