Can you summarize the following document?
        
  一個監視器包含:  一個監視器的程式在執行一個執行緒前會先取得互斥鎖,直到完成執行緒或是執行緒等待某個條件被滿足才會放棄互斥鎖。若每個執行中的執行緒在放棄互斥鎖之前都能保證不變數成立,則所有執行緒皆不會導致競態條件成立。  以下這個銀行賬戶的提款/存款事務的監視器是個簡單的例子:  當一個執行緒執行管程中的一個子程式時,稱為佔用(occupy)該管程. 管程的實現確保了在一個時間點,最多隻有一個執行緒佔用了該管程。這是管程的互斥鎖訪問性質。  當執行緒要呼叫一個定義在管程中的子程式時,必須等到已經沒有其它執行緒在執行管程中的某個子程式。  在管程的簡單實現中,編譯器為每個管程物件自動加入一把私有的互斥鎖。該互斥鎖初始狀態為解鎖,在管程的每個公共子程式的入口給該互斥鎖加鎖,在管程的每個公共子程式的出口給該互斥鎖解鎖。  這個例子中的不變數是「任何操作執行前 balance 變數必須反映正確的餘額」。一般而言,不變數的條件不被寫在程式中,而在註解中有相關說明,然而Eiffel程式設計語言顯式檢查不變數。

  對於許多應用場合,互斥操作是不夠用的。執行緒可能需要等待某個條件formula_1為真,才能繼續執行。在一個忙碌等待迴圈中  將會導致所有其它程序都無法進入臨界區使得該條件formula_1為真,該管程發生死鎖.  解決辦法是條件變數(condition variables). 概念上,一個條件變數就是一個執行緒佇列(queue), 其中的執行緒正等待某個條件變為真。每個條件變數formula_4關聯著一個斷言formula_5. 當一個執行緒等待一個條件變數,該執行緒不算作佔用了該管程,因而其它執行緒可以進入該管程執行,改變管程的狀態,通知條件變數formula_4其關聯的斷言formula_5在當前狀態下為真.  因此對條件變數存在兩種主要操作:  在下述例子中, 用管程實現了一個訊號量. 一個私有整型變數codice_4需要被互斥訪問。管程中定義了子程式“增加”(V)與子程式“減少”(P),整型變數codice_4不能被減少到小於0; 因此子程式“減少”必須等到該整型變數是正數時才可執行. 使用條件變數codice_6與相關聯的斷言formula_10.  當一個通知(signal)發給了一個有執行緒處於等待中的條件變數,則有至少兩個執行緒將要佔用該管程: 發出通知的執行緒與等待該通知的某個執行緒. 只能有一個執行緒佔用該管程,因此必須做出選擇。兩種理論體系導致了兩種不同的條件變數的實現:

  東尼·霍爾與泊·派克·漢森最早提出的是阻塞式條件變數. 發出通知(signaling)的執行緒必須等待被通知(signaled)的執行緒放棄佔用管程(或者離開管程,或者等待某個條件變數)。使用阻塞式條件變數的管程被稱為"霍爾風格"(Hoare-style)管程或"通知且急迫等待"(signal-and-urgent-wait)管程.  設每個管程物件有兩個執行緒佇列 設對於每個條件變數formula_4, 有一個執行緒佇列 這些佇列會公平(fair)排程,甚至實現為先進先出.  各個環節實現如下 (規定各個環節彼此是互斥的. 因此restart一個執行緒,並不會立即執行,直到當前環節完成)  codice_10子程式選擇下一個執行緒佔用管程,如果沒有候選的執行緒則解鎖管程.  發出通知的執行緒轉入等待,但會比線上程入口的佇列有更高優先權被排程,這稱為""通知且急迫等待""。另一種方案是""通知且等待"",不設codice_4佇列,發出通知的執行緒進入codice_7佇列等待.  某些實現提供了signal and return操作.  如果在每個signal formula_4的開始處,formula_5為真, 那麼在wait formula_4的結尾處formula_5也應為真。 這可由契約式設計來表達. 在這些契約中, formula_26是管程的不變數.  在上述契約中,設定formula_26 and formula_5不依賴於任何佇列長度.  如果可以查詢條件變數所關聯的佇列上處於等待的執行緒的數量,可以使用更為複雜的契約。例如,一個有用的契約對,無需不變數就允許管程的佔用被傳遞  參見 Howard and Buhr "et al.,"有更多資訊。 特別需要注意,斷言formula_5完全是由程式設計者負責,程式設計者需要在頭腦中保持對斷言有一致的(consistent)定義。  下例是用阻塞式管程實現一個有界的、執行緒安全的棧. 即多執行緒併發訪問這個棧時,在任意時刻最多隻有一個執行緒執行push或pop操作。

  "非阻塞式條件變數" (也稱作""Mesa風格""條件變數或""通知且繼續""(signal and continue)條件變數), 發出通知的執行緒並不會失去管程的佔用權. 被通知的執行緒將會被移入管程入口的codice_7佇列. 不需要codice_4佇列。pthread中的條件變數就是這種非阻塞式:要先顯式獲得互斥加鎖(pthread_mutex_lock),呼叫pthread_cond_wait時隱式對互斥鎖解鎖並進入阻塞睡眠,被喚醒後還要再顯式獲得互斥加鎖。  非阻塞式條件變數經常把signal操作稱作notify —. 也常用notify all操作把該條件變數關聯的佇列上所有的執行緒移入codice_7佇列.  各種操作定義如下. (規定各種操作都是互斥的,執行緒被restart並不會立即執行,直到發起的操作完成)  一個變種實現,把被通知的(notified)執行緒移入佇列codice_16, 具有比codice_7更高的優先順序. 參見 Howard and Buhr "et al."。  可以把斷言formula_5關聯於條件變數formula_4,因而formula_5codice_18返回時期望為真. 但是,這必須確保發出通知的執行緒結束到被通知的執行緒恢復執行這一時間段內,formula_5保持為真. 這一時間段內可能會有其它執行緒佔用過管程。因此通常必須把每個wait操作用一個迴圈包裹起來:  其中formula_1是一個條件,強於formula_5. 操作codice_19與codice_20被視為"提示"(hints)某個等待佇列的formula_1可能為真. 上述迴圈的每一次迭代,表示失去了一次通知。  一個銀行賬戶的例子,取款執行緒將等待資金已經完成注入賬戶之後再執行。  在這個例子中,被等待的條件是取款金額大於存款餘額,存款執行緒不可能知道取款金額,因此存款執行緒發出的通知的意涵是提示所有等待中的取款執行緒檢查它自身的斷言是否為真。

  Java程式設計語言中,每個物件都可以作為一個管程。需要互斥使用的方法必須明確標示關鍵字synchronized。 程式碼塊也可以標示關鍵字synchronized。  不使用明確的條件變數, Java的這種管程在入口佇列之外,使用單獨的條件等待佇列. 所有等待的執行緒進入這個佇列,所有的notify與notify all操作也施加於這個佇列。這種方法已經被其它程式設計語言使用,如C#。

  另一種實現方法是忽略signal操作。當一個執行緒交出管程佔用權(returning或者waiting),所有處於等待狀態的執行緒的斷言都被檢查,直到發現某個執行緒的斷言為真。在這種系統中,不需要條件變數,但是斷言必須明確編碼。 wait操作的契約:

  pthread中,條件變數實際上是一個阻塞執行緒處於睡眠狀態的執行緒佇列。每個條件變數都必須與一個(且建議只能是一個)互斥鎖配套使用。一個執行緒首先獲得互斥鎖,然後檢查或者修改“條件”;如果條件不成立,則呼叫pthread_cond_wait(),依次實施3個操作: 執行緒離開“條件”的臨界區時,必須對當前互斥鎖執行解鎖。

  從Windows Vista開始,Windows Thread用critical section與條件變數配合,二者均為使用者態同步物件,不能跨程序使用。使用API函式InitializeConditionVariable建立條件變數;函式SleepConditionVariableCS用於釋放臨界區並阻塞在條件變數上;函式WakeConditionVariable或 WakeAllConditionVariable喚醒掛在條件變數上的執行緒。 也可配套使用讀寫鎖(Slim Reader/Writer (SRW) Locks)。

  假喚醒是POSIX Threads與Windows API使用條件變數時可能發生的複雜情形。一個掛在條件變數上的執行緒被signaled,正在等待的條件仍有可能不成立。假喚醒(Spurious wakeup)是指即使沒有執行緒signaled該條件變數,掛在該條件變數上的執行緒卻被喚醒。因此,應該用while迴圈包圍條件變數等待操作:

  被偷走的喚醒是POSIX Threads與Windows API使用條件變數時,執行緒呼叫g_cond_signal時,另一個執行緒已經獲取了mutex使得期望的條件不再滿足,因此被喚醒的執行緒面臨著條件不成立。因此,應該用while迴圈包圍條件變數等待操作.

  東尼·霍爾與泊·派克·漢森在1972年形成了管程的構思, 根據他們自己更早的想法與艾茲赫爾·戴克斯特拉的工作.  泊·派克·漢森第一個實現了管程。 東尼·霍爾發展了理論框架並證明了與訊號量等價。  管程不久用於單任務作業系統的程序間通訊.  已經支援管程的程式設計語言:  許多庫已經允許在程式設計語言沒有本地支援時構建管程。當庫呼叫時,程式設計者負責明確表示互斥執行的程式碼塊的開始與結尾. Pthreads就是這樣一個庫.
管程 (英語:Monitors,也稱為監視器) 是一種程式結構,結構內的多個子程式(物件或模組)形成的多個工作執行緒互斥訪問共享資源。這些共享資源一般是硬體或一群變數。管程實現了在一個時間點,最多隻有一個執行緒在執行管程的某個子程式。與那些通過修改資料結構實現互斥訪問的併發程式設計相比,管程實現很大程度上簡化了程式設計。