关于哲学家就餐问题的程序代码分析
- 格式:pdf
- 大小:61.46 KB
- 文档页数:2
利用信号量机制解决哲学家进餐的问题c语言哲学家进餐问题是经典的并发编程问题,主要是模拟多个哲学家同时在桌子上进行吃饭和思考。
由于每个哲学家都需要同时拿起左右两边的餐叉才能进餐,如果不加以控制,容易导致死锁的情况发生。
为了解决这个问题,可以利用信号量机制来确保每个哲学家同时只能拿到一把餐叉,从而避免死锁的发生。
首先,我们需要定义一些全局变量和信号量来表示哲学家和餐叉的状态。
```c#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <semaphore.h>#define N 5 // 哲学家的数量pthread_t philosophers[N]; // 哲学家的线程pthread_mutex_t forks[N]; // 餐叉的互斥锁sem_t room; // 哲学家就餐的信号量void *philosopher(void *arg) {int id = *(int *)arg;int right = id;int left = (id + 1) % N;while (1) {// 思考sleep(rand() % 3 + 1);// 请求餐叉sem_wait(&room);pthread_mutex_lock(&forks[right]);pthread_mutex_lock(&forks[left]);// 进餐sleep(rand() % 3 + 1);printf("Philosopher %d is eating\n", id + 1);// 放下餐叉pthread_mutex_unlock(&forks[left]);pthread_mutex_unlock(&forks[right]);sem_post(&room);}}int main() {int i, id[N];// 初始化互斥锁和信号量sem_init(&room, 0, N - 1); // 初始信号量为N-1,表示有N-1个座位可供就餐for (i = 0; i < N; i++) {pthread_mutex_init(&forks[i], NULL);}// 创建哲学家线程for (i = 0; i < N; i++) {id[i] = i;pthread_create(&philosophers[i], NULL, philosopher, &id[i]); }// 等待哲学家线程结束for (i = 0; i < N; i++) {pthread_join(philosophers[i], NULL);}// 销毁互斥锁和信号量for (i = 0; i < N; i++) {pthread_mutex_destroy(&forks[i]);}sem_destroy(&room);return 0;}```在主函数中,我们首先定义了哲学家和餐叉的线程以及信号量。
哲学家就餐问题代码哲学家就餐问题是一个经典的并发编程问题,描述了五位哲学家围坐在圆桌旁,每个哲学家面前有一碗米饭和一只筷子。
哲学家的生活由思考和就餐两个活动组成,思考时不需要筷子,但就餐时需要同时拿起自己左右两边的筷子才能进餐。
问题在于如何设计算法,使得每位哲学家都能够顺利地进行思考和就餐,而不会发生死锁。
以下是一个简单的解决方案,使用Java语言实现:java.import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;class Philosopher implements Runnable {。
private int id;private Lock leftChopstick;private Lock rightChopstick;public Philosopher(int id, Lock leftChopstick, Lock rightChopstick) {。
this.id = id;this.leftChopstick = leftChopstick;this.rightChopstick = rightChopstick;}。
private void think() {。
System.out.println("哲学家 " + id + " 正在思考");try {。
Thread.sleep((long) (Math.random() 1000));} catch (InterruptedException e) {。
e.printStackTrace();}。
}。
private void eat() {。
leftChopstick.lock();rightChopstick.lock();System.out.println("哲学家 " + id + " 正在就餐");try {。
课程设计题目进程同步模拟设计—哲学家就餐学院计算机科学与技术专业计算机科学与技术班级计算机姓名指导教师20011 年 1 月19 日需求分析1.1问题描述有五个哲学家围坐在一圆桌旁,桌中央有一盘通心粉,每人面前有一只空盘子,每两人之间放一只筷子,即共5只筷子。
每个哲学家的行为是思考和进餐。
为了进餐,每个哲学家必须拿到两只筷子,并且每个人只能直接从自己的左边或右边去取筷子。
思考时则同时将两支筷子放回原处(此图中以叉子代表筷子)规则:只有拿到两只筷子时,哲学家才能吃饭;如果筷子已经在他人手上,则该哲学家必须等到他人吃完之后才能拿到筷子;任何一个哲学家在自己没有拿到两只筷子吃饭之前,决不放下自己手中的筷子。
由此出现的问题:可能出现死锁问题,因为当五个哲学家都饥饿时,都拿着一支筷子,这样就可能五个哲学家都用不上餐1.2问题分析该问题可用记录型信号量或者是AND型信号量解决。
记录型信号量解决:经分析可知,放在桌子上的筷子是临界资源,在一段时间内只允许一位哲学家使用,为了实现对筷子的互斥使用,可以用一个信号量表示一只筷子,由这五个信号量组成信号量数组。
当哲学家饥饿时总是先拿其左边的筷子,成功后,再去拿右边的筷子,又成功后方可就餐。
进餐完,又先放下他左边的筷子,再放下右边筷子。
这个算法可以保证不会有两个相邻的哲学家同时就餐,但有可能引起死锁。
AND型信号量解决:在哲学家就餐过程中,要求每个哲学家先获得两个临界资源后方能就餐,这在本质上就是AND同步问题,故用AND信号量机制可获得最简洁的解法。
1.3解决方法对于死锁问题可采取这样的几种解决方法:(1)至多只允许四个哲学家同时进餐,以保证至少有一个哲学家可以进餐,最终总会释放出他所用过的两只筷子,从而可使更多的哲学家进餐;(2)仅当左右两只筷子均可用时,才允许哲学家拿起筷子就餐(3)规定奇数号哲学家先拿起右边筷子,然后再去拿左边筷子,而偶数号哲学家则相反。
(4)把筷子顺序编号 fk0, fk1, fk2, fk3, fk4,给每个哲学家分配筷子时,必须依从小号到大号(或者相反顺序)进行。
利用管程解决哲学家进餐问题用三种不同状态表示哲学家的活动:进餐、饥饿、思考。
(thinking,hungry,eating)state[5];为第i个(i=0,..,4)哲学家设置一个条件变量self[i],当哲学家饥饿又不能获得筷子时,用self[i].wait来阻塞自己。
condition self[5];管程设置三个函数:pickup取筷子,putdown放筷子,test测试是否具备进餐条件。
?Monitor DP;(thinking, hungry, eating) state[5] ;condition self[5];void Entry pickup(int i) /*第i号哲学家取筷子*/{ state[i] = hungry; test(i);if(state[i]!=eating) self[i].wait;}void Entry putdown(int i) /*第i号哲学家放下筷子*/{ state[i] = thinking;test((i+4)%5); test((i+1)%5);}void test(int i) /*测试第i号哲学家是否具备进餐条件*/{if (state[(i+4) % 5]<>eating) && (state[i]=hungry) && (state[(i+1) % 5]<>eating){ state[i] = eating; self[i].signal; }}{ (for i = 0; i < 5; i++) state[i] = thinking;}cobeginvoid philosopher(int i){while(true){thinking; DP.pickup(i); eating;DP.putdown(i); }}coend。
关于哲学家就餐问题的分析代码.①总体思路: 都去拿左边的筷⼦,并且最后⼀个⼈不能去拿筷⼦(防⽌⼤家都拿了左边的筷⼦,没有右边的筷⼦,导致死锁了),解决死锁问题的办法就是同时只允许四位哲学家同时拿起同⼀边的筷⼦,这样就能保证⼀定会有⼀位哲学家能够拿起两根筷⼦完成进⾷并释放资源,供其他哲学家使⽤,从⽽实现永动,避免了死锁。
举个最简单的栗⼦,假定0~3号哲学家已经拿起了他左边的筷⼦,然后当4号哲学家企图去拿他左边的筷⼦的时候,将该哲学家的线程锁住,使其拿不到其左边的筷⼦,然后其左边的筷⼦就可以被3号哲学家拿到,然后3号哲学家进餐,释放筷⼦,然后更多的哲学家拿到筷⼦并进餐。
如何才能实现当4号哲学家企图拿起其左边的筷⼦的时候将该哲学家的线程阻塞?这个时候就要⽤到该问题的提出者迪杰斯特拉(这货还提出了迪杰斯特拉最短路径算法,著名的银⾏家算法也是他发明的)提出的信号量机制。
因为同时只允许有四位哲学家同时拿起左筷⼦,因此我们可以设置⼀个信号量r,使其初始值为4,然后每当⼀位哲学家企图去拿起他左边的筷⼦的时候,先对信号量做⼀次P操作,从⽽当第五位哲学家企图去拿做筷⼦的时候,对r做⼀次P操作,r = -1,由r < 0得第五位哲学家的线程被阻塞,从⽽不能拿起左筷⼦,因此也就避免了死锁问题。
然后当哲学家放下他左边的筷⼦的时候,就对r做⼀次V操作。
②在主线程和⼦线程中,避免了竞争关系.代码参考如下:/************************************ @file linux_zexuejia.c* @copyright ⽉光下的脚步 Co.,Ltd.ALL Right Reserved* @brief 使⽤信号量和互斥锁完成,哲学家就餐问题。
* @author 王有康* @data 2019、7、31* @version V1.1* @Last Modified 2018/10/25 wangyoukang* @note 当前版本是v1.1版本,以后需要修改,可以在上添加版本和修改⽇期* @note* @warning* @Function List :************************************/#include <stdio.h>#include <stdlib.h>#include <malloc.h>#include <time.h>#include <unistd.h>#include <pthread.h>#include <semaphore.h>#define N 5//互斥锁pthread_mutex_t chops[N];//信号量sem_t r;/*********************** @fn philosopher* @biref 每次只有4个⼈能够获得左⼿资源,这样不会死锁* @param philosopher number* @return* @other***********************/void philosopher(void *arg){int i = *(int *)arg;int left = i;int right = (i + 1)%N;while(1){printf("哲学家%d在思考问题\n",i);usleep(1000);printf("哲学家%d饿了\n",i);sem_wait(&r);pthread_mutex_lock(&chops[left]);printf("philosopher %d take left lock\n",i);pthread_mutex_lock(&chops[right]);printf("philosopher %d take right lock\n",i);printf("philosopher %d eat\n",i);usleep(1000);pthread_mutex_unlock(&chops[right]);printf("philosopher %d release right lock\n",i);pthread_mutex_unlock(&chops[left]);printf("philosopher %d realease left lock\n",i);sem_post(&r);}}/*********************** @fn philosopher* @biref 主函数* @param* @return* @other***********************/int main(int argc,char **argv){int i = 0,*ptr;pthread_t tid[N];for(i = 0;i < N;i++){pthread_mutex_init(&chops[i],NULL);}sem_init(&r,0,4);for(i = 0;i < N;i++){//防⽌主线程和对等线程竞争,给线程和主线程分配不同的内存区域。
进程通信-经典进程问题哲学家进餐哲学家进餐1.至多只能允许有四个哲学家同时进餐,保证至少有一个哲学家能够进餐,最终总会释放出他所使用过的两支筷子,从而可使更多的哲学家进餐。
伪码:semaphore chopstick[5]={1,1,1,1,1};semaphore room=4;void philosopher(int i){while(true){think();wait(room); //请求进入房间进餐wait(chopstick[i]); //请求左手边的筷子wait(chopstick[(i+1)%5]); //请求右手边的筷子eat();signal(chopstick[(i+1)%5]); //释放右手边的筷子signal(chopstick[i]); //释放左手边的筷子signal(room); //退出房间释放信号量room}}2.仅当哲学家的左右两支筷子都可使用时,才允许他拿起两只筷子进餐。
伪码:semaphore mutex = 1 ;semaphore chopstick[5]={1,1,1,1,1};void philosopher(int i){while(true){think();wait(mutex);wait(chopstick[(i+1)]%5);wait(chopstick[i]);signal(mutex);eat();signal(chopstick[(i+1)]%5);signal(chopstick[i]);}}读者和写者Reader:beginrepeatwait(s);wait(rmutex);if readcount=0 then wait(wmutex);readcount:=readcount+1;signal(rmutex);signal(s);Perform read operation;wait(rmutex)readcount:=readcount-1;if readcount=0 then signal(wmutex);signal(rmutex);until false;endWrite:beginrepeatwait(mutex)if writecount=0 then wait(s);writecount:=Writecount+1;signal(mutex);wait(wmutex);Perform write operation;signal(wmutex);wait(mutex);writecount:=writecount-1;if writecount=0 then signal(s);signal(mutex);until false;end(1)情况一:当读者A正在运行时,读者B、写者A、写者B三个进程被阻塞在哪里?读者B被阻塞在wait(s)写者A、写者B被阻塞在wait(wmutex)(2)情况二:当写者A正在运行时,读者A、读者B、写者B三个进程被阻塞在哪里?读者A、读者B被阻塞在wait(s)写者B被阻塞在wait(wmutex)。
计算机操作系统_哲学家就餐问题_C++实现源代码//哲学家就餐问题的解法#include <windows.h>#include <process.h>#include <time.h>#include <stdlib.h>#include <stdio.h>#include <iostream>using namespace std; //命名空间std内定义的所有标识符都有效const unsigned int PHILOSOPHER_NUM=5; //哲学家数目const char THINKING=1; /*标记当前哲学家的状态,1表示等待,2表示得到饥饿,3表示正在吃饭*/const char HUNGRY=2;const char DINING=3;HANDLE hPhilosopher[5]; //定义数组存放哲学家/*HANDLE(句柄)是windows操作系统中的一个概念。
指的是一个核心对象在某一个进程中的唯一索引*/HANDLE semaphore[PHILOSOPHER_NUM]; // semaphore 用来表示筷子是否可用HANDLE mutex; // Mutex用来控制安全输出DWORD WINAPI philosopherProc( LPVOID lpParameter) //返回 DWORD(32位数据)的 API 函数philosopherProc{int myid;char idStr[128];char stateStr[128];char mystate;int ret;unsigned int leftFork; //左筷子unsigned int rightFork; //右筷子myid = int(lpParameter);itoa(myid, idStr, 10);WaitForSingleObject(mutex, INFINITE);cout << "philosopher " << myid << " begin......" << endl; ReleaseMutex(mutex);mystate = THINKING; //初始状态为THINKINGleftFork = (myid) % PHILOSOPHER_NUM;rightFork = (myid + 1) % PHILOSOPHER_NUM;while (true){switch(mystate){case THINKING:mystate = HUNGRY; // 改变状态strcpy(stateStr, "HUNGRY");break;case HUNGRY:strcpy(stateStr, "HUNGRY");ret = WaitForSingleObject(semaphore[leftFork], 0); // 先检查左筷子是否可用if (ret == WAIT_OBJECT_0){ret = WaitForSingleObject(semaphore[rightFork], 0); //左筷子可用就拿起,再检查右筷子是否可用if (ret == WAIT_OBJECT_0){mystate = DINING; // 右筷子可用,就改变自己的状态strcpy(stateStr, "DINING");}else{ReleaseSemaphore(semaphore[leftFork], 1, NULL); // 如果右筷子不可用,就把左筷子放下}}break;case DINING:// 吃完后把两支筷子都放下ReleaseSemaphore(semaphore[leftFork], 1, NULL);ReleaseSemaphore(semaphore[rightFork], 1, NULL);mystate = THINKING; // 改变自己的状态strcpy(stateStr, "THINKING");break;}// 输出状态WaitForSingleObject(mutex, INFINITE);cout << "philosopher " << myid << " is : " << stateStr << endl;ReleaseMutex(mutex);// sleep a random time : between 1 - 5 sint sleepTime;sleepTime = 1 + (int)(5.0*rand()/(RAND_MAX+1.0));Sleep(sleepTime*10);}}int main(){int i;srand(time(0));mutex = CreateMutex(NULL, false, NULL);for (i=0; i<PHILOSOPHER_NUM; i++){semaphore[i] = CreateSemaphore(NULL, 1, 1, NULL);hPhilosopher[i]=CreateThread(NULL,0,philosopherProc,LPVOID(i), CREATE_SUSPENDED,0);}for (i=0; i<PHILOSOPHER_NUM; i++)ResumeThread(hPhilosopher[i]);Sleep(2000);return 0;}。
关于哲学家就餐问题的程序代码分析:
A. Dining.c
变量说明:
HINSTANCE hInst应用实例句柄
HWND hWndMain主窗口句柄
HBITMAP hbmpOffscreen位图句柄
BOOL bWaitMultiple用于选择等待的方式(要么死锁,要么
一直循环运行)
BOOL bFastFood;用于选择进程等待的时间
extern int gDinerState[]用于表示哲学家的状态(外部接口) extern int gChopstickState[]用于表示筷子的状态
extern HANDLE
gchopStick[PHILOSOPHERS]
筷子的数组
函数说明:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)处理消息函数:
根据进程调度的消息的不同处理不同的消息包括:关闭窗口,销毁资源,画窗口,和重画窗口等
BOOL CreateOffscreen()获得当前窗口的一个位图句柄
void RenderOffscreen(HDC hDestDC)通过位图句柄画演示的界面
1.先画桌子,再画筷子,再画盘子,
2.再画哲学家:
哲学家用圆表示,根据哲学家的状态选择画
笔(为resting 状态时为绿色圆圈,为等待
或者,就餐时为红色圆圈)
3.最后画哲学家的手:
判断的依据是若筷子的编号和哲学家的编
号一致,那么认为这根筷子属于这个哲学家
(筷子资源空闲)那么可以画手表示哲学家
获得了这个筷子。
(获得资源)
BOOL
InitApplication(HINSTANCE
hInstance)
初始化应用窗口
BOOL
InitInstance(HINSTANCE hInstance, int nCmdShow)初始化主窗口
弹出选择对话框进行模式选择,并启动进程
int PASCAL
WinMain(HINSTANCE
hInstance, HINSTANCE
hPrevInstance,
LPSTR lpCmdLine, int
nCmdShow)
调用前面的函数并对进线程消息进行处理
B.Mutex.c
变量说明:
extern HWND hWndMain在Dining.c中定义是这个文件的外部接口extern BOOL bWaitMultiple在Dining.c中定义是这个文件的外部接口extern BOOL bFastFood在Dining.c中定义是这个文件的外部接口
int gDinerState[PHILOSOPHERS] 哲学家状态数组
int gChopstickState[PHILOSOPHERS] 筷子状态数组
HANDLE
gchopStick[PHILOSOPHERS]
筷子状态数组
函数说明:
1. DWORD WINAPI PhilosopherThread(LPVOID pVoid)
哲学家进程:
用于分配给哲学家筷子(资源)通过开始运行时候的对话框的选择来控制资源的分配方式:两中分配方式见二、三所述。
通过随机产生的时间来控制线程的运行,每个哲学家的状态都是resting waiting eating,一直循环。
2.int Diner(void)
用于建立互斥的信号对象(筷子)和启动哲学家进程。