語言: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名哲學家,每兩名哲學家之間的桌上擺著一根筷子,兩根筷子中間是一碗公尺飯,如圖所示 圖來自於王道作業系統課本 哲學家只會思考和進餐,哲學家在思考時,並不影響其他人。只有當哲學家飢餓時,才試圖拿起左 右兩根筷子 一根一根拿起 若筷子在其他人手上,則需等待。飢餓的哲學家只有同時拿...