C 多執行緒程式設計 經典模型 哲學家進餐問題

2021-08-14 05:09:16 字數 4046 閱讀 6344

語言:c#

總起:

今天的哲學家進餐問題是最後多執行緒模型,討論的是在有限的資源裡執行緒競爭導致死鎖、飢餓等問題。

沒有接觸過多執行緒程式設計的同學,可以先看一下第一章。

哲學家進餐問題:

該問題說的是,有5個哲學家圍在乙個圓桌前進餐,每個哲學家兩旁有兩把叉子,一共5把叉子。每個哲學家進行進餐需要拿起左右兩把叉子,吃完之後將兩把叉子放回供其他人使用。

這個是wiki上的:

根據以上的描述,我寫了如下的程式:

static random random = new random();

public static readonly int max_philosophers_num = 5; // 最大的哲學家數量

// 同步標記

static listforks = new list();

private static listphilosophers = new list();

// 獲取左側的叉子

public static void waitleftfork(int number)

// 獲取右側的叉子

public static void waitrightfork(int number)

// 開始吃飯

public static void eat(int number)

// 釋放兩個叉子

public static void releasetwoforks(int number)

// 休息

public static void takearest(int number)

static void main(string args)

for (int i = 0; i < max_philosophers_num; i++)

}// 哲學家

static void philosopher(object num)

}

這邊是沒有做同步,每個哲學家按照如下的順序進行進餐:

1.獲取左邊的叉子

2.獲取右邊的叉子

3.進餐

4.釋放兩個叉子

5.休息

執行一下看看結果:

很明顯發生了死鎖,所有的哲學家都在第一時間拿取了左側的叉子,導致所有人都無法拿取右側的叉子。

資源層級解決方案:

導致問題的原因是只有5把叉子,但有5個哲學家嘗試進餐。如果同一時間保證只有4個哲學家嘗試進餐,那應該就能解決該問題了。

改進後的**如下:

static semaphore eatings = new semaphore(max_philosophers_num - 1, max_philosophers_num - 1);

// 哲學家

static void philosopher2(object num)

}

增加了乙個訊號量使一次只有4個程序嘗試進餐。

結果如下:

可以看到在前4個哲學家開始進餐的時候,最後乙個哲學家不會嘗試進餐,從而解決了該問題。

中間人解決方案:

如果保證在拿起兩把叉子的時候,這兩個操作是同步的,那就保證了該哲學家必然能進行進餐,從而不會發生資源競爭的情況。

讓我們來試試:

static mutex middle = new mutex();

// 哲學家

static void philosopher3(object num)

}

可以看到結果,一切是多麼有序的在進行:

chandy/misra解決方案:

但是第二種有個比較明顯的缺陷,就是沒有解決飢餓問題,其他哲學家可能已經等了很久,結果剛吃完飯的人又連續去吃,這樣可能導致其他哲學家始終吃不到飯。

而chandy/misra的解決方案是未用餐的人優先的,所以沒有以上的缺陷。

我簡單描述一下該解決方案的內容:叉子有一種叫做dirty的屬性,首先初始時所有的叉子都是dirty的。哲學家可以向周圍的人請求叉子,這時如果手中的叉子是dirty的,那就可以洗乾淨之後交給請求者,否者則保留在手中。進餐之後兩把叉子都會變為dirty。

因為給了一次對方後叉子洗乾淨了,所以不會在兩個人之間來回傳遞叉子。

以下是我自己理解的**:

class fork

// 判斷當前叉子是否是自己的

public bool isowner(philosopher p)

// 鎖住當前叉子

public void lock()

// 解鎖當前叉子

public void unlock()

// 使用完之後叉子變髒

public void doinguse()

// 請求乙個叉子

public void request(philosopher wanter)

unlock();

philosopher.mutex.releasemutex();}}

}// philosopher.cs

class philosopher

public void start()

void run()

else

// 吃

eat(id);

// 釋放叉子

leftfork.doinguse();

rightfork.doinguse();

leftfork.unlock();

rightfork.unlock();

// 休息一下

takearest(id);}}

// 開始吃飯

public static void eat(int number)

// 休息

public static void takearest(int number)

}// program.cs

class program

// 建立5個哲學家

for (int i = 0; i < max_philosophers_num; i++)

// 初始化叉子,這邊是關鍵

for (int i = 0; i < max_philosophers_num; i++)

// 開始跑

for (int i = 0; i < max_philosophers_num; i++)}}

結果如下:

可以看出能夠很好的執行。該方案主要針對沒有吃過的哲學家優先進行優化,如果沒有必要的話其實第二種方案絕對是足夠了,不然為什麼課程上教的都是第二種方案(笑)。

總結:

多執行緒的課程到此結束了,在多執行緒問題中,很多都是執行1小時、1天、甚至好幾天才會出一次的問題,但如果縱容這種錯誤是一種不專業的行為。

多執行緒程式設計始終算作是一種比較難的程式設計,所以盡量編寫單執行緒,避免不了的情況下請多使用資源複製的方式,如果還是不行就最好參考多執行緒的這三種模型,大多數多執行緒的問題都是這三個模型或其變種。

總之完成多執行緒程式設計後一定要多多測試,這邊給出幾個多執行緒的測試工具:aspect-oriented framework、cglib、asm和contest。

如果以後有機會接觸多執行緒的話,會繼續研究,現在就到此為止。

哲學家多執行緒問題

問題描述 一圓桌前坐著5位哲學家,兩個人中間有乙隻筷子,桌子 有麵條。哲學家思考問題,當餓了的時候拿起左右兩隻筷子吃飯,必須拿到兩隻筷子才能吃飯。上述問題會產生死鎖的情況,當5個哲學家都拿起自己右手邊的筷子,準備拿左手邊的筷子時產生死鎖現象。解決辦法 1 新增乙個服務生,只有當經過服務生同意之後才能...

PV操作經典例題 哲學家進餐問題

哲學家進餐問題 五個哲學家共用一張圓桌,分別坐在周圍的五張椅子上,在桌子上有五隻碗和五隻筷子,他們的生活方式是交替地進行思考和進餐。平時,乙個哲學家進行思考,飢餓時便試圖取用其左右最靠近他的筷子,只有在他拿到兩隻筷子時才能進餐。進餐畢,放下筷子繼續思考。分析 放在桌子上的筷子是臨界資源,在一段時間內...

經典程序同步問題 哲學家進餐

問題描述 一張圓桌上坐著5名哲學家,每兩名哲學家之間的桌上擺著一根筷子,兩根筷子中間是一碗公尺飯,如圖所示 圖來自於王道作業系統課本 哲學家只會思考和進餐,哲學家在思考時,並不影響其他人。只有當哲學家飢餓時,才試圖拿起左 右兩根筷子 一根一根拿起 若筷子在其他人手上,則需等待。飢餓的哲學家只有同時拿...