哲學家就餐問題詳解

FangYwang發表於2020-12-23

2.23 哲學家就餐問題

1965年由Dijkstra提出

問題描述

  • 5個哲學家圍坐圓桌
  • 5只餐叉間隔擺放
  • 哲學家行為為思考或進餐
  • 哲學家進餐必須同時使用兩邊的餐叉
  • 思考時蔣餐叉放回原處
  • 兩個哲學家不使用同一把叉子
  • 儘量避免死鎖和飢餓

如圖
在這裡插入圖片描述


方案一 取左叉

都去拿左邊的叉子,死鎖

semaphore fork[5] = {1, 1, 1, 1, 1};

void main()
{
        cobegin {philosopher(0); 
				 philosopher(1); 
				 philosopher(2);
				 philosopher(3); 	
				 philosopher(4);
				}coend;
}

void philosopher(int i)
{
         while(true) {
                think;                      //思考
                wait(fork[i]);           //拿起左邊的叉子
                wait(fork[(i+1)%5]);     //拿起右邊的叉子
                 eat();
                signal(fork[i]);         //放回左邊的叉子
                signal(fork[(i+1)%5]);   //放回右邊的叉子
          }
}

方案二

請求同時拿兩個叉子,被佔則等待隨機事件再請求,可能活鎖

semaphore fork[5] = {1, 1, 1, 1, 1};

void main()
{
        cobegin {philosopher(0); 
				 philosopher(1); 
				 philosopher(2);
				 philosopher(3); 	
				 philosopher(4);
				}coend;
}

void philosopher(int i)
{
    while(true) {
    think;              //思考
    wait(fork[i]);          //拿起左邊的叉子
    timeout(wait(fork[(i+1)%5], [0, T]) //若右邊的叉子被佔用,則放下左邊叉,等待一段隨機時間後再拿                           
    eat();                                                               
    signal(fork[i]);    	//放回左邊的叉子
    signal(fork[(i+1)%5]);  //放回右邊的叉子
          }
}

方案三 資源分級

為資源(這裡是餐叉)分配一個偏序(partial order)或者分級(hierarchy)的關係,並約定所有資源都按照這種順序獲取,按相反順序釋放,而且保證不會有兩個無關資源同時被同一項工作所需要

① 為餐叉編號
  • 就餐前,先取用編號較低的餐叉,再取用編號較高的餐叉
  • 就餐畢,先放下編號較高的餐叉,再放下編號較低的餐叉
semaphore fork[5] = {1, 1, 1, 1, 1};
void main()
{
        cobegin {philosopher(0); 
				 philosopher(1); 
				 philosopher(2);
				 philosopher(3); 	
				 philosopher(4);
				}coend;
}
void philosopher(int i)
{
   while(true) {
      think();  //思考
      if (i  !=  4) {
           wait(fork[i]); wait(fork[(i+1)%5]);} //先左後右
      else {
           wait(fork[(i+1)%5]); wait(fork[i]);} //先右後左
      eat(); 
      if (i  !=  4) {
           signal(fork[(i+1)%5]); signal(fork[i]);} //先右後左
      else {
           signal(fork[i]); signal(fork[(i+1)%5]);} //先左後右
   }
}
② 為哲學家編號
  • 奇數號的哲學家必須首先拿左邊的餐叉
  • 偶數號的哲學家必須首先拿右邊的餐叉
semaphore fork[5] = {1, 1, 1, 1, 1};
void main()
{
        cobegin {philosopher(0); 
				 philosopher(1); 
				 philosopher(2);
				 philosopher(3); 	
				 philosopher(4);
				}coend;
}

void philosopher(int i)
{
    while(true) {
       think();  //思考
       if (i % 2 != 0) {
           wait(fork[i]); wait(fork[(i+1)%5]);} //先左後右
       else {
           wait(fork[(i+1)%5]); wait(fork[i]);}
       eat(); 
       signal(fork[(i+1)%5]);                   //先右後左
       signal(fork[i]);   
    }
}

方案四 服務生方法

  • 引入一個餐廳服務生,哲學家必須經過他的允許才能拿起餐叉
  • 最多允許4個哲學家同時進食
semaphore fork[5] = {1, 1, 1, 1, 1}, room = 4;
void main()
{
       cobegin {philosopher(0); 
				 philosopher(1); 
				 philosopher(2);
				 philosopher(3); 	
				 philosopher(4);
				}coend;
}

void philosopher(int i)
{
         while(true) {
                think;                          //思考
                wait(room);                     //佔據就餐位置
                wait(fork[i]);                  //拿起左邊的叉子
                wait(fork[(i+1)%5]);            //拿起右邊的叉子
                signal(fork[i]);                //放回左邊的叉子
                signal(fork[(i+1)%5]);          //放回右邊的叉子
                signal(room);                   //釋放就餐位置
          }
}

引申:And型訊號量集

  • 在一個原語中申請需要的多個臨界資源,要麼全部分,要麼一個都不分配
  • AND型訊號量集P原語為Swait(Simultaneous Wait),V原語為Ssignal(Simultaneous Signal)。
  • Swait(S1, S2, …, Sn)
  • Ssignal(S1, S2, …, Sn)

管程方案

monitor dining controller;
cond ForkReady [5];					/*condition variable for synchronization */
boolean fork[ 5]={ true } ; 		/*availability status of each fork*/

void get_forks (int pid)			/*pid is the philosopher id number */
{
	int left = pid;
	int right =(+pid)%5;
	/*grant the left fork*/
	if( ! fork [ left])
		cwait(ForkReady [ left] );	/*queue on condition variable */
	fork [ left]= false ;
	/*grant the right fork*/
	if( ! fork [ right] )
		cwait(ForkReady [right] );
	/*queue on condition variable */
	fork [ right] = false;
}

void release_forks ( int pid){
	int left = pid;
	int right =(++pid)%5;
	/*release the left fork*/
	if( empty (ForkReady [ leftl)	/*no one is waiting for this fork */
		fork [ left] = true;
	else							/*awaken a process waiting on this fork */
		csignal (ForkReady [ left]);
	/*release the right fork*/
	if (empty (ForkReady [right])	/*no one is waiting for this fork*/
		fork [ right]= true;
	else							/*awaken a process waiting on this fork*/
		csignal (ForkReady [right] );
}

void philosopher [ k=0 to 4]		/*the five philosopher clients */
{
	while (true){
		<think>;
		get_forks (k);				/*client requests two forks via monitor * /
		<eat spaghetti>;
		release_forks (k);			/*client releases forks via the monitor */
	}
}

在這裡插入圖片描述

相關文章