哲學家進餐問題

哲學家進餐問題描述:

哲學家進餐問題是E.W.Dijkstra 在1965年秋,爲埃因霍溫(Eindhoven)技術大學學生提出的一個考題,原題爲五胞胎進餐問題,不久就以牛津大學教授Hoare(霍爾)所取得名字——“哲學家進餐問題”而聞名。經過中國化得哲學家進餐問題可以這樣描述:5個哲學家同坐在一張圓桌旁,每個人的面前放着一碗麪條,碗的兩旁各擺放一根筷子。假設哲學家的生活除了吃飯就是思考(這是一種抽象,即對該問題而言,其他活動無關緊要),而吃飯的時候要左手拿一根筷子,右手拿一根筷子,然後開始進餐。吃完後又將筷子放回原處,繼續思考問題。

一個哲學家的活動進程描述:

 

  1. 思考問題
  2. 餓了停止思考,左手拿一根筷子(如果左側哲學家已持有它,則需要等待);
  3. 右手拿一根筷子(如果右側哲學家已持有它,則需要等待);
  4. 進餐
  5. 放右手筷子
  6. 放左手筷子
  7. 重新回到思考問題的狀態分別考慮下面兩種情況:
  • 按哲學家的活動進程,當所有的哲學家都同時拿起左手的筷子時,則所有的哲學家都將拿不到右手的筷子,並處於等待狀態,那麼哲學家都將無法進餐,最終餓死。
  • 將哲學家的活動進程修改一下,變爲當右手的筷子拿不到時,就放下左手的筷子,這種情況不一定沒有問題。因爲可能在一瞬間,所有的哲學家都同時拿起左手的筷子,則自然拿不到右手的筷子,於是都同時放下左手的筷子,等一會,又同時拿起左手的筷子,如此這樣永遠重複下去,則所有的哲學家都將無法進餐。

以上兩個方面的問題,其實反映的是程序併發執行時進程同步的兩個問題,一個是死鎖,一個是飢餓。

下面有三種方法避免死鎖:

  • 最多允許4位哲學家同時拿起左手(或右手)的筷子。
  • 僅當哲學家的左、右兩支筷子均可用時,才允許他同時拿起左、右手的兩支筷子;否則一支筷子也不拿。
  • 奇數號哲學家先拿右邊的筷子,然後再取左邊的筷子;偶數號哲學家先拿左邊的筷子,然後再拿右邊的筷子。 

 代碼1:僅當哲學家的左、右兩支筷子均可用時,才允許他同時拿起左、右手的兩支筷子;否則一支筷子也不拿

#include<windows.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
HANDLE mutex;//互斥變量
HANDLE chopstick[5];
HANDLE philosopher[5];
int num=0;
int random()
{
	return rand()%100+60;
}
void eating(int id)
{
	int etime=random();
	Sleep(etime);
    printf("\t\t\t哲學家%d號吃了%d秒飯\n",id,etime);
}
//用windows下的WINAPI函數實現簡單的多線程,PVOID無類型指針;param參數
DWORD WINAPI phthread(LPVOID param)  
{
	num++;
int	id=num;
int limittime=0;//進餐的次數限制
int chopstick1,chopstick2;//實現左右2根筷子變量
while(true)
{
	Sleep(200);//未來100毫秒內不會被喚醒;Unix系統使用的是時間片法,而Windows則屬於搶佔式,將進程掛起
    if(limittime>=1)
    break;
chopstick1=WaitForSingleObject(chopstick[(id+1)%5],0),	chopstick2=WaitForSingleObject(chopstick[id],0);//同時拿起筷子
if(chopstick1==WAIT_OBJECT_0&&chopstick2==WAIT_OBJECT_0)
{

	WaitForSingleObject(mutex,INFINITE);//一直佔主線程,到ReleaseMutex(mutex)爲止
    printf("哲學家%d號拿到兩隻筷子開始吃飯。\n", id);
    ReleaseMutex(mutex);//釋放互斥信號體
    limittime++;
    WaitForSingleObject(mutex,INFINITE);
    eating(id);
    ReleaseMutex(mutex);//釋放互斥信號體
    WaitForSingleObject(mutex,INFINITE);
    printf("\t\t\t哲學家%d號吃完飯啦,放下筷子,繼續思考。\n", id);
    ReleaseMutex(mutex); 
}
    ReleaseSemaphore(chopstick[(id+1)%5], 1, NULL),ReleaseSemaphore(chopstick[id], 1, NULL);//同時放下筷子
    WaitForSingleObject(mutex,INFINITE);
    ReleaseMutex(mutex);
}
return 0 ;
}
int main()
 {
   srand((unsigned)time(0));
   mutex = CreateMutex(NULL, false, NULL);//創建互斥信號量,傳遞false值
   for (int i = 0; i < 5; ++i)
   {
       chopstick[i]=CreateSemaphore(NULL,1,1,NULL);//創建筷子的信號量
   }
   for (i = 0; i < 5; ++i)
   {
      
       philosopher[i] = CreateThread(NULL, 0, phthread,NULL, 0, NULL);//創建哲學家新線程,線程名爲phthread,傳遞值爲空,返回HANDLE
   }
   Sleep(10000);
   for (i = 0; i < 5; ++i)
   {

       CloseHandle(philosopher[i]);
       CloseHandle(chopstick[i]);
   }
   CloseHandle(mutex);
   Sleep(500);
   return 0;
}


	

 代碼2:

奇數號哲學家先拿右邊的筷子,然後再取左邊的筷子;偶數號哲學家先拿左邊的筷子,然後再拿右邊的筷子。

#include<windows.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
HANDLE mutex;//互斥變量
HANDLE chopstick[5];
HANDLE philosopher[5];
int num;
int random()
{
	return rand()%100+60;
}
void eating(int id)
{
	int etime=random();
	Sleep(etime);
    printf("\t\t\t哲學家%d號吃了%d秒飯\n",id,etime);
}
//用windows下的WINAPI函數實現簡單的多線程,PVOID無類型指針;param參數
DWORD WINAPI phthread(LPVOID param)  
{
	num++;
int limittime=0;//進餐的次數限制
int id=num;//哲學家的號數
int chopstick1,chopstick2;//實現左右2根筷子變量
while(true)
{
	Sleep(100);//未來100毫秒內不會被喚醒;Unix系統使用的是時間片法,而Windows則屬於搶佔式
if(limittime>=1)
break;
if(id%2!=0)
{
	//奇數號的哲學家先拿起右邊的筷子再拿起左邊的筷子;
	chopstick1=WaitForSingleObject(chopstick[(id+1)%5],0);//等待筷子發出信號,如果條件滿足,則返回WAIT_OBJECT_0 ,否則返回WAIT_TIMEOUT
	if(chopstick1==WAIT_OBJECT_0)
	{
		chopstick2=WaitForSingleObject(chopstick[id],0);
		if(chopstick2==WAIT_OBJECT_0)
		{
			WaitForSingleObject(mutex,INFINITE);//一直佔主線程,到ReleaseMutex(mutex)爲止
            printf("哲學家%d號拿到兩隻筷子開始吃飯。\n", id);
            ReleaseMutex(mutex);//釋放互斥信號體
            limittime++;
            WaitForSingleObject(mutex,INFINITE);
            eating(id);
            ReleaseMutex(mutex);//釋放互斥信號體
            WaitForSingleObject(mutex,INFINITE);
            printf("\t\t\t哲學家%d號吃完飯啦,放下筷子,繼續思考。\n", id);
            ReleaseMutex(mutex);
            ReleaseSemaphore(chopstick[id], 1, NULL);//釋放當前左邊筷子的信號量
		}
		//如果哲學家搶到一隻筷子,在搶佔另一隻筷子時失敗,則要放棄已經搶佔到的資源。
        ReleaseSemaphore(chopstick[(id+1)%5], 1, NULL);//釋放當前右邊筷子的信號量
	}
}
else
{
	//偶數號哲學家先拿起左邊的筷子,再拿起右邊的筷子
	chopstick1=WaitForSingleObject(chopstick[id],0);
	if(chopstick1==WAIT_OBJECT_0)
	{
		chopstick2=WaitForSingleObject(chopstick[(id+1)%5],0);
		if(chopstick2==WAIT_OBJECT_0)
		{
			//一直佔主線程,到ReleaseMutex(mutex)爲止
			WaitForSingleObject(mutex,INFINITE);
			printf("哲學家%d號拿到兩隻筷子開始吃飯。\n", id);
            ReleaseMutex(mutex);//釋放互斥信號體,不然其他哲學家拿不到筷子
            limittime++;
            WaitForSingleObject(mutex,INFINITE);
            eating(id);
            ReleaseMutex(mutex);//釋放互斥信號體
            WaitForSingleObject(mutex,INFINITE);
            printf("\t\t\t哲學家%d號吃完飯啦,放下筷子,繼續思考。\n", id);
            ReleaseMutex(mutex);
            //左右兩邊都搶到筷子的哲學家,吃完放後釋放資源
            ReleaseSemaphore(chopstick[(id+1)%5], 1, NULL);//釋放當前右邊筷子的信號量
		}
		//如果哲學家搶到一隻筷子,在搶佔另一隻筷子時失敗,則要放棄已經搶佔到的資源。
        ReleaseSemaphore(chopstick[id], 1, NULL);//釋放當前左邊筷子的信號量
	}
}
  WaitForSingleObject(mutex,INFINITE);
  ReleaseMutex(mutex);
}
return 0 ;
}
int main()
 {
   srand((unsigned)time(0));
   //3個參數lpMutecAttributes:指向SECURITY_ATTRIBUTES型態的結構指針,NULL使用默認安全
   //blnitialOwner BOOL:建立互斥體,互斥體同時只能一個線程擁有
   //lpName String:指定互斥體對象的名子,爲NULL不指定
   mutex = CreateMutex(NULL, false, NULL);//創建互斥信號量,傳遞false值
   for (int i = 0; i < 5; ++i)
   {
       //4個參數分別爲lpSemaphoreAttributes:爲信號量的屬性,一般設置爲NULL
	   //lInitialCount:信號量初始值,爲0 默認爲unsignal狀態,爲 1 默認爲signal狀態
	   //lpMaximumCount:信號量的最大值爲1
	   //lpName:信號量的名字,可設置爲NULL
       chopstick[i]=CreateSemaphore(NULL,1,1,NULL);//創建筷子的信號量
   }
   for (i = 0; i < 5; ++i)
   {
       //6個參數分別爲:lpThreadAttributes:指向SECURITY_ATTRIBUTES型態的結構指針,NULL使用默認安全
	   //dwStackSize:設置初始棧的大小,爲0 默認將使用與調用該函數的線程相同的棧空間大小
	   //lpStartAdress:指向線程函數的指針,函數名
	   //lpParameter:向線程函數傳遞參數,不需要傳遞參數是爲NULL
	   //dwCreationFlags:線程標誌,爲 0 時,表示創建後立即激活
	   //lpThreadld:保存新線程的id,返回線程id,不想返回id設置爲NULL
       philosopher[i] = CreateThread(NULL, 0, phthread,NULL, 0, NULL);//創建哲學家新線程,線程名爲phthread,傳遞值爲空,返回HANDLE
   }
   Sleep(10000);
   for (i = 0; i < 5; ++i)
   {

       CloseHandle(philosopher[i]);
       CloseHandle(chopstick[i]);
   }
   CloseHandle(mutex);
   Sleep(500);
   return 0;
}


	

 

 

發佈了15 篇原創文章 · 獲贊 10 · 訪問量 2230
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章