無聊的哲學家進餐問題

1 描述

一張圓桌上坐着5名哲學家,每兩個哲學家之間的桌上擺一根筷子,桌子的中間是一碗米飯,如圖所示。哲學家們傾注畢生精力用於思考和進餐,哲學家在思考時,並不影響他人。只有當哲學家飢餓的時候,才試圖拿起左、右兩根筷子(一根一根地拿起)。若是筷子已在他人手上,則需等待。飢餓的哲學家只有同時拿到了兩根筷子才能夠開始進餐,當進餐完畢後,放下筷子繼續思考。算法

2 分析

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

  • 至多容許四個哲學家同時進餐;
  • 僅當一個哲學家左右兩邊的筷子均可用時才容許他抓起筷子;
  • 對哲學家順序編號,要求奇數號哲學家先抓左邊的筷子,而後再轉他右邊的筷子,而偶數號哲學家恰好相反。

3 解法

3.1 解法一

假設當一個哲學家左右兩邊的筷子均可用時,才容許他抓起筷子。進程

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;  
    }
}

3.2 解法二

至多隻容許有四位哲學家同時去拿左邊的筷子,最終能保證至少有一位哲學家可以進餐,並在用完時能釋放出他用過的兩隻筷子,從而使更多的哲學家可以進餐。資源

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); //釋放信號量給其餘捱餓的哲學家
    }
}

3.3 解法三

僅當哲學家的左右兩隻筷子都可使用,才容許他拿起筷子進餐。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]) ;  //放回左邊筷子

    }
}

3.4 解法四

規定奇數號哲學家先拿他左邊的筷子,而後在去拿右邊的筷子;而偶數號哲學家則相反。按此規定,將是一、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]) ;  //放回右邊筷子
        
        }
    }
}

3.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]); 
    }
}

4 拓展

5個哲學家問題本質上是解決併發程序中的死鎖和飢餓,能夠將推廣爲更通常的n個進程,m個共享資源的問題。

相關文章
相關標籤/搜索