【专项专攻】01-哲学家进餐问题

前言:

image.png

当一位哲学家思考时,他与其他同事不交流。时而,他会感到饥饿,并试图拿起与他相近的两根筷子(筷子在他和他的左或右邻居之间)。一个哲学家一次只能拿起一根筷子。显然,他不能从其他哲学家手里拿走筷子。当一个饥饿的哲学家同时拥有两根筷子时,他就能吃。在吃完后,他会放下两根筷子,并开始思考。

哲学家就餐问题是一个经典的同步问题,这不是因为其本身的实际重要性,也不是因为计算机科学家不喜欢哲学家,而是因为它是大量并发控制问题的一个例子。这个代表型的例子满足:在多个进程之间分配多个资源,而且不会出现死锁和饥饿。

哲学家进餐问题:
(一)问题:
5个哲学家围坐在一个圆桌上,每两个哲学家之间都有一只筷子,哲学家平时进行思考,只有当他们饥饿时,才拿起筷子吃饭。
规定每个哲学家只能先取其左边筷子,然后取其右边筷子,然后才可以吃饭。

(二)分析:
每一只筷子都是一个临界资源,设置5个互斥信号量。
Semaphore stcik[5]={1,1,1,1,1}

因为:只有占有左边筷子-》占有右边筷子-》吃饭
所以p(左边筷子)-》p(右边筷子)-》吃饭

(三)实现:
main(){
philosopher(0);//注意不可以用循环,因为此中是4个并列的进程。
philosopher(1);
philosopher(2);
philosopher(3);
philosopher(4);
}

philosopher(int i){
while(1){
思考;
p(stick[i]);//取左边筷子
p(stick[i+1]);//取右边筷子

进餐;
V(stick[i]);
v(stick[i+1]);
}
}

(四)问题:
如果5个哲学家同时拿起自己左边的筷子,就会发生死锁。

(五)防止死锁的方法:

(1)方法一:
规定在拿到左侧的筷子后,先检查右面的筷子是否可用。如果不可用,则先放下左侧筷子, 等一段时间再重复整个过程。

问题:发生饥饿现象;
如果同时拿起左边筷子,则右边的筷子都不可用,则放下,然后再次拿起,。。。,则谁都无法就餐,

(2)方法二:
最多允许四个哲学家同时进餐,以保证至少有一个哲学家能够进餐,最终总会释
放出他所使用过的两支筷子,从而可使更多的哲学家进餐。

伪码:
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
}
}

(3)将拿左筷子,与拿右筷子当做一个原子操作:(即只有当左右筷子均可以拿到时,才拿筷子。)

方法一:
利用AND 型信号量机制实现:根据课程讲述,在一个原语中,将一段代码同时需
要的多个临界资源,要么全部分配给它,要么一个都不分配,因此不会出现死锁的情形。

伪码:
semaphore chopstick[5]={1,1,1,1,1};
void philosopher(int I)
{
while(true)
{
think();
P(chopstick[(I+1)]%5,chopstick[I]);
eat();
V(chopstick[(I+1)]%5,chopstick[I]);
}
}

方法二:
利用信号量的保护机制实现。通过信号量mutex对eat()之前的取左侧和右侧筷
子的操作进行保护,使之成为一个原子操作,这样可以防止死锁的出现。

伪码:
semaphore mutex = 1 ;
semaphore chopstick[5]={1,1,1,1,1};
void philosopher(int I)
{
while(true)
{
think();
P(mutex);
P(chopstick[(I+1)]%5);
P(chopstick[I]);
V(mutex);

eat();
V(chopstick[(I+1)]%5);
V(chopstick[I]);
}
}

(4)规定奇数号的哲学家先拿起他左边的筷子,然后再去拿他右边的筷子;而偶数号 的哲学家则相反。
伪码:
semaphore chopstick[5]={1,1,1,1,1};
void philosopher(int i)
{
while(true)
{
think();
if(i%2 == 0) //偶数哲学家,先右后左。
{
P (chopstick[ i + 1 ] mod 5) ;
P (chopstick[ i]) ;
eat();
V (chopstick[ i + 1 ] mod 5) ;
V (chopstick[ i]) ;
}
Else //奇数哲学家,先左后右。
{
p (chopstick[ i]) ;
p (chopstick[ i + 1 ] mod 5) ;
eat();
V (chopstick[ i]) ;
V (chopstick[ i + 1 ] mod 5) ;
}
}

你可能感兴趣的:(【专项专攻】01-哲学家进餐问题)