一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭,如图所示。哲学家们倾注毕生精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿的时候,才试图拿起左、右两根筷子(一根一根地拿起)。若是筷子已在他人手上,则需等待。饥饿的哲学家只有同时拿到了两根筷子才能够开始进餐,当进餐完毕后,放下筷子继续思考。算法
1) 关系分析。 5名哲学家与左右邻居对其中间筷子的访问是互斥关系。
2) 整理思路。 显然这里有五个进程。本题的关键是如何让一个哲学家拿到左右两个筷子而不形成死锁或者饥饿现象。那么解决方法有两个,一个是让他们同时拿两个筷子;二是对每一个哲学家的动做制定规则,避免饥饿或者死锁现象的发生。数组
3) 信号量设置。 定义互斥信号量数组Ch0PstiCk[5] = {l, 1, 1, 1, 1}用于对5个筷子的互斥访问。对哲学家按顺序从0~4编号,哲学家i左边的筷子的编号为i,哲学家右边的筷子的编号为(i+l)%5。并发
semaphore chopstick[5] = {1,1,1,1,1}; //定义信号量数组chopstick[5],并初始化 Pi(){ //i号哲学家的进程 while(1){ P(chopstick[i]); //取左边筷子 P(chopstick[(i+1)%5]); //取右边篌子 eat; V(chopstick[i]); //放回左边筷子 V(chopstick[(i+1)%5]); //放回右边筷子 think; } }
该算法存在如下问题:当五个哲学家都想要进餐,分别拿起他们左边筷子的时候(都刚好执行完wait(chopstick[i]);)筷子已经被拿光了,等到他们再想拿右边的筷子的时候(执行 wait(chopstick[(i+l)%5]);)就全被阻塞了,这就出现了死锁。code
为了防止死锁的发生,能够对哲学家进程施加一些限制条件,好比:blog
假设当一个哲学家左右两边的筷子均可用时,才容许他抓起筷子。进程
semaphore chopstick[5] = {1,1,1,1,1}; //初始化信号量 semaphore mutex=l; //设置取筷子的信号量 Pi(){ //i号哲学家的进程 while(1){ P(mutex); //在取筷子前得到互斥量,一次只能由一个哲学家取筷子 P(chopstick[i]) ; //取左边筷子 P(chopstick[(i+1)%5]); //取右边筷子 V(mutex); //释放取筷子的信号量 eat; V(chopstick[i]); //放回左边筷子 V(chopstick[(i+1)%5]); //放回右边筷子 think; } }
至多只容许有四位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家可以进餐,并在用完时能释放出他用过的两只筷子,从而使更多的哲学家可以进餐。资源
semaphore chopstick[5] = {1,1,1,1,1}; //初始化信号量 semaphore eating = 4; //至多只容许四个哲学家能够同时进餐 Pi(){ //i号哲学家的进程 while(1){ think; P(eating); //请求进餐,如果第五个则挨饿 P(chopstick[i]); //取左边筷子 P(chopstick[(i+1)%5]) ; //取右边筷子 eat; V(chopstick[(i+1)%5]) ; //放回右边筷子 V(chopstick[i]) ; //放回左边筷子 V(eating); //释放信号量给其余挨饿的哲学家 } }
仅当哲学家的左右两只筷子都可使用,才容许他拿起筷子进餐。it
semaphore chopstick[5] = {1,1,1,1,1}; //初始化信号量 semaphore mutex = 1; //设置取筷子的信号量 Pi(){ //i号哲学家的进程 while(1){ think; P(mutex); //在去筷子前得到互斥量 P(chopstick[i]); //取左边筷子 P(chopstick[(i+1)%5]) ; //取右边筷子 V(mutex); //释放互斥量 eat; V(chopstick[(i+1)%5]) ; //放回右边筷子 V(chopstick[i]) ; //放回左边筷子 } }
规定奇数号哲学家先拿他左边的筷子,而后在去拿右边的筷子;而偶数号哲学家则相反。按此规定,将是一、2号哲学家竞争1号筷子;三、4号哲学家竞争3号筷子。map
即5位哲学家都先竞争奇数号筷子,得到后,再去竞争偶数号筷子,最后总会有一位哲学家可以得到两只筷子而进餐。请求
semaphore chopstick[5] = {1,1,1,1,1}; //初始化信号量 Pi(){ //i号哲学家的进程 while(1){ think; if(i%2==0){ P(chopstick[(i+1)%5]) ; //取右边筷子 P(chopstick[i]); //取左边筷子 eat; V(chopstick[(i+1)%5]) ; //放回右边筷子 V(chopstick[i]) ; //放回左边筷子 }else{ //奇数哲学家,先左后右 P(chopstick[i]); //取左边筷子 P(chopstick[(i+1)%5]) ; //取右边筷子 V(mutex); //释放互斥量 eat; V(chopstick[i]) ; //放回左边筷子 V(chopstick[(i+1)%5]) ; //放回右边筷子 } } }
采用AND型信号量机制来解决,即要求每一个哲学家先得到两个临界资源(筷子)后方能进餐。
semaphore chopstick[5] = {1,1,1,1,1}; //初始化信号量 semaphore mutex = 1; //设置取筷子的信号量 Pi(){ while(1){ think; P(chopstick[i],chopstick[(i+1)%5]); eat; V(chopstick[i],chopstick[(i+1)%5]); } }
5个哲学家问题本质上是解决并发程序中的死锁和饥饿,能够将推广为更通常的n个进程,m个共享资源的问题。