Peterson 問題
Peterson 解法很好地描述瞭解決臨界區問題的演算法,並闡明瞭設計滿足互斥、進展和有限等待要求的軟體中涉及的一些複雜性。
do { flag[i] = true; turn = j; while (flag[j] && turn == j); /* critical section */ flag[i] = false; /* remainder section */ } while (true);
Peterson 解法中程序 Pi 的結構。此解法僅限於兩個交替執行臨界區和剩餘區的程序。程序編號為 P0 和 P1。為方便起見,當存在 Pi 時,我們使用 Pj 表示另一個程序;即 j 等於 1 − I。Peterson 解法要求這兩個程序共享兩個資料項:
int turn; boolean flag[2];
變數 turn 表示輪到哪個程序進入其臨界區。即,如果 turn == i,則允許程序 Pi 在其臨界區中執行。如果程序準備進入其臨界區,則使用標誌陣列來指示。例如,如果 flag[i] 為真,則此值表示 Pi 準備進入其臨界區。在對這些資料結構的解釋完成後,我們現在可以描述上面所示的演算法。為了進入臨界區,程序 Pi 首先將其 flag[i] 設定為 true,然後將 turn 設定為值 j,從而斷言如果另一個程序希望進入臨界區,則可以這樣做。如果兩個程序同時嘗試進入,則 turn 將大致同時設定為 i 和 j。最終只有一個賦值會發生;另一個會發生,但會立即被覆蓋。turn 的最終值決定了這兩個程序中哪個程序首先允許進入其臨界區。我們現在證明此解法是正確的。我們需要證明:
- 保持互斥。
- 滿足進展要求。
- 滿足有限等待要求。
為了證明 1,我們注意到每個 Pi 僅當 flag[j] == false 或 turn == i 時才進入其臨界區。還要注意,如果兩個程序可以同時在其臨界區中執行,則 flag[0] == flag[1] == true。這兩個觀察結果表明,P0 和 P1 不可能在大致相同的時間成功執行其 while 語句,因為 turn 的值可以是 0 或 1,但不能同時是兩者。因此,其中一個程序(例如,Pj)必須成功執行 while 語句,而 Pi 必須至少執行一個附加語句(“turn == j”)。但是,那時,flag[j] == true 且 turn == j,並且只要 Pj 處於其臨界區,此條件就會持續存在;因此,保持互斥。
為了證明特性 2 和 3,我們注意到,如果一個程序卡在 while 迴圈中,條件為 flag[j] == true 且 turn == j,則只能阻止程序 Pi 進入臨界區;這是唯一可能的迴圈。如果 Pj 不準備進入臨界區,則 flag[j] 將為 == false,並且 Pi 可以進入其臨界區。如果 Pj 已設定 flag[j] = true 並也在其 while 語句中執行,則 turn == i 或 turn == j。如果 turn == i,則 Pi 將進入臨界區。如果 turn == j,則 Pj 將進入臨界區。儘管一旦 Pj 退出其臨界區,它將把 flag[j] 重置為 false,從而允許 Pi 進入其臨界區。如果 Pj 將 flag[j] 重置為 true,則 Pj 還必須將 turn 設定為 i。因此,由於 Pi 在執行 while 語句時不會更改變數 turn 的值,因此 Pi 將最多在 Pj 進入一次後進入臨界區(進展)(有限等待)。
缺點
Peterson 解法適用於兩個程序,但此解法是使用者模式下臨界區最佳方案。
此解法也是一種忙等待解法,因此會浪費 CPU 時間。因此可能會出現**“自旋鎖”**問題。任何忙等待解法都可能出現此問題。