Peterson算法概述
Peterson算法是一種實現進程/線程間互斥訪問臨界區的算法。(線程間共享內存地址空間,進程需要采用共享內存實現)
關鍵術語:
臨界區:一段代碼,進程/線程在這段代碼中進程將訪問共享資源,當另外一個進程已在這段代碼運行時,其他進程就不能在這段代碼中運行。
互斥:當一個進程/線程在臨界區訪問共享資源時,其他進程/線程不能進入臨界區訪問任何其他共享資源的情形。
wiki定義:
Peterson's algorithm (or Peterson's solution) is a concurrent programming algorithm for mutual exclusion that allows two or more processes to share a single-use resource without conflict, using only shared memory for communication. It was formulated by Gary L. Peterson in 1981. While Peterson's original formulation worked with only two processes, the algorithm can be generalized for more than two.
Peterson算法實現
該算法使用兩個變量,flag
和 turn
。 flag[n]
值為 true
表示進程 n
想要進入臨界區。turn表示現在輪到誰,是一個進程編號。
int flag[2];
int turn;
void init() {
flag[0] = flag[1] = 0; // 1->thread wants to grab lock
turn = 0; // whose turn? (thread 0 or 1?)
}
void lock() {
flag[self] = 1; // self: thread ID of caller
turn = 1 - self; // make it other thread's turn
while ((flag[1-self] == 1) && (turn == 1 - self))
; // spin-wait
}
void unlock() {
flag[self] = 0; // simply undo your intent
}
算法解釋:
flag[self] = 1
:設置自己進程感興趣,想要訪問臨界區。
turn = 1 - self
:將turn設置為對方進程。注意這個turn是個共享變量,若多進程/多線程進行訪問,會保留最后一次寫的turn值,前面寫的值被寫覆蓋了(overwriting)。然后是一個自旋等待(CPU空轉,忙等待,busy wait):while ((flag[1-self] == 1) && (turn == 1 - self));
對該自旋等待真值表進行分析,如下:
flag[1-self] == 1 | turn == 1 - self | 真值 | 含義 |
---|---|---|---|
T | T | T | 對方進程也想訪問臨界區,且turn值為自己設定 |
T | F | F | 對方進程也想訪問臨界區,且turn值為對方設定 |
F | T | F | 只有自己想訪問臨界區,直接訪問即可。 |
F | F | F | 只有自己想訪問臨界區,直接訪問即可。 |
由上述真值表可見,僅有雙方(兩個進程/線程)都想訪問臨界區時,才會出現自旋情況。將上述情況以單核CPU情況模擬,有如下兩種情況。
- P0先寫turn值而P1后寫:P1自旋,P0進入臨界區
Process 0 | Process 1 | turn值 | 事件 |
---|---|---|---|
lock() flag[0] = 1; turn = 1; |
1 | 調度程序調度P0執行 | |
lock() flag[1] = 1; turn = 0; while ((flag[0] == 1) && (turn == 0)); P1自旋 |
0 | 中斷,調度程序調度P1執行 | |
進入臨界區 do something 出臨界區 unlock() flag[0] = 0; |
0 | 中斷,調度程序調度P0執行 | |
while ((flag[0] == 1) && (turn == 0)); 自旋結束進入臨界區 do something 出臨界區 unlock() flag[1] = 0; |
0 | 中斷,調度程序調度P1執行 |
- P1先寫turn值而P0后寫:P0自旋,P1進入臨界區
Process 0 | Process 1 | turn值 | 事件 |
---|---|---|---|
lock() flag[1] = 1; turn = 0; |
0 | 調度程序調度P1執行 | |
lock() flag[0] = 1; turn = 1; while ((flag[1] == 1) && (turn == 1)); P1自旋 |
1 | 中斷,調度程序調度P0執行 | |
進入臨界區 do something 出臨界區 unlock() flag[1] = 0; |
1 | 中斷,調度程序調度P1執行 | |
while ((flag[1] == 1) && (turn == 1)); 自旋結束進入臨界區 do something 出臨界區 unlock() flag[0] = 0; |
1 | 中斷,調度程序調度P0執行 |
由上述例子可見:並發時,兩進程/線程中存在着某種搶占的關系,即誰先寫入turn值,就不會因此而自旋(因為自旋條件為turn為對面值);若不是並發,則可以直接進入,而后上鎖的進程/線程則需要等待先上鎖進程/線程解鎖。
算法評價
Peterson算法是一種不依賴硬件實現的鎖機制。如今大多數CPU以指令亂序執行來提高執行效率,此時實現Peterson算法就得使用相關內存屏障指令。現在一般使用硬件支持的鎖機制(比如test-and-set或compare-and-swap),這些機制往往只需要很少的硬件支持。
reference
[1] wiki
[2] 操作系統導論
[3] 現代操作系統
[4] 深入理解計算機系統