行程同步與臨界區間問題

監督程式(monitor)

赤裸的號誌與條件變數威力強大卻容易出錯:漏掉一個 signal、把 wait 和 signal 對調、少寫一個 release,你就會得到一個可能幾個月後才浮現的微妙錯誤。監督程式是一個更高階的構造,通常內建在程式語言裡,它把這些危險的零件打包在一起,並替你強制它們被正確使用。日常的畫面是一間有櫃台接待員的私人辦公室:所有對共用資料的存取都得經過監督程式的程序,而接待員保證任一時刻裡頭只有一位訪客。你不再自己分發鑰匙;結構替你做了。

具體而言,監督程式把三樣東西捆成一個單元:共用資料、操作它的那些程序、以及一把隱含的互斥鎖。它的定義性規則是:任一時刻最多只有一條執行緒在監督程式裡活動——互斥對每個監督程式程序都是自動的,所以你從不手寫 lock 與 unlock,也從不會忘記它們。為了等待條件,監督程式提供帶有 wait 與 signal 的條件變數:一條無法前進的執行緒對某個條件呼叫 wait,這會釋放監督程式的互斥鎖並睡去,讓另一條執行緒進來;稍後某條執行緒呼叫 signal 把它喚醒。由於進入與離開都由語言處理,一整類的上鎖錯誤根本寫不出來。

一個真正微妙之處,是 signal 發生時會怎樣,因為此時有兩條執行緒都想當監督程式裡那唯一活動的一條:發信號者,以及剛被喚醒的等待者。不同的監督程式設計給出不同的答案。在「signal 後等待」(Hoare 語意)下,發信號者讓到一旁,等待者立刻執行。在「signal 後繼續」(Mesa 語意,多數真實系統採用,包括 Java 與 pthreads 風格的監督程式)下,發信號者繼續執行,等待者只有在稍後能重新取得監督程式時才恢復。常見的 Mesa 風格在實務上的後果,正是條件變數那條規則:因為在 signal 與等待者真正執行之間,世界可能已經改變,所以等待者必須在 while 迴圈裡重新檢查條件,絕不能只因為「被 signal 過」就假設條件為真。

一個有限緩衝區的監督程式提供 insert(item) 與 remove():兩者自動互斥。insert 做 while (count == size) wait(notFull); ... signal(notEmpty)。remove 做 while (count == 0) wait(notEmpty); ... signal(notFull)。任何地方都不會出現手寫的 lock/unlock——由監督程式供應。

監督程式 = 共用資料 + 程序 + 自動互斥 + 條件變數,全在一處。

多數真實的監督程式採用 Mesa(signal 後繼續)語意,所以被 signal 的執行緒在執行時不保證條件仍成立——要用 while 重新檢查,與裸用條件變數時一模一樣。

又称
monitor construct監視器管程