使用訊號量Semaphore實現沒有飢餓問題的鎖

Broken__Ice發表於2020-12-15

參考LittleBookOfSemaphores中的Morris’s Solution

原理及程式碼實現

設定鎖屬性如下
鎖屬性
我們設定有三個執行緒waiting空間:room1,room2,room3
waiting空間
其中room3是隱式設定的,裡面同一時間只能有一個執行緒,也可以說是互斥的

初始化鎖如下
初始化鎖
我們的原理如下

  1. 先將一段時間內發出獲取鎖請求的執行緒都放入room1,也就是阻塞在圖中程式碼第48行
  2. 由於訊號量s1初始值為1,第一個進入room1的執行緒可以繼續執行48行之後的內容,它可以進入room2,也就是被阻塞在圖中程式碼第67行的位置。
    它在room2中進行了一個操作,就是判斷自己之後是否還有執行緒在room1中被阻塞,若有的話,隨機喚醒一個進入room2,若沒有了,就通知room2中的執行緒可以依次進入room3
  3. room1中的執行緒全部進入room2,最後一個執行緒發出s2的post,room2中的隨機一個執行緒被喚醒,在圖中程式碼第69行開始繼續執行,離開room2(進入room3),執行執行緒的主體程式,然後釋放鎖
  4. 釋放鎖時,執行緒會回頭判斷身後room2中是否還有執行緒被阻塞,若有,隨機喚醒一個進入room3,若沒有了,就通知這段時間內被阻塞在room1門外也就是圖中程式碼第42行的執行緒可以依次進入room1

獲取鎖
獲取鎖
釋放鎖
釋放鎖

通過這一種方式,我們一批一批處理請求獲得鎖的執行緒,緩解了有執行緒從一開始就被阻塞,直到幾乎所有執行緒都執行完才輪到執行的飢餓問題。

效果驗證

為了證明確實較好地解決了飢餓問題,我設定一個陣列starve,用來儲存各個執行緒的飢餓值。

  1. 當有一個執行緒執行acquire鎖的時候,該執行緒的飢餓值+1,且所有執行緒的飢餓值乘2
    飢餓1
  2. 當執行緒執行release鎖的時候,對應飢餓值清0。
    飢餓2
  3. 當我們執行room3中的執行緒主體部分時,列印所有執行緒的當前飢餓情況,並累加到一個sum值中,執行全部執行緒結束後列印總飢餓度sum。同時我們也通過maxx變數記錄最大飢餓值。
    飢餓3

執行結果:
飢餓測試1
總飢餓值為77208,最大飢餓度為1024

這樣看好像還看不出什麼,但是我設定了一個會產生飢餓的鎖進行比對
簡單的單訊號量存在飢餓問題的互斥鎖:
飢餓鎖
也是一樣的飢餓演算法
飢餓測試2
最大飢餓度就達到誇張的10173741824!!

兩者相比就能看出優化效果之好了

相關文章