對于 Pi:
do {
turn = i; // prepare enter section
while(turn==j);
//critical section
turn = j; //exit section.
} while(true);
對于 Pj:
do {
turn = j; // prepare enter section
while(turn==i);
//critical section
turn = i; //exit section.
} while(true);
在這個簡化的演算法中,如果行程 i 想進入 i 的臨界區,它將設定“turn = i”(不同于 Peterson 的解決方案,它將設定“turn = j”)。這個演算法似乎不會導致死鎖或饑餓,那么為什么彼得森的演算法沒有像這樣簡化呢?
另一個問題:據我所知,信號量 P/V 操作等互斥機制需要原子性(P 應該同時測驗 sem.value 和 sem.value)。但是為什么上面的演算法只使用一個變數turn似乎不需要原子性(turn = i, test turn == j not atomicity)?
uj5u.com熱心網友回復:
在詢問演算法是否避免死鎖和饑餓之前,您首先必須驗證它是否仍然處于鎖定狀態。使用您的版本,即使假設順序一致性,操作也可以按如下順序排列:
Pi Pj
turn = i;
while (turn == j); // exits immediately
turn = j;
while (turn == i); // exits immediately
// critical section // critical section
你有一個鎖沖突。
對于您的第二個問題:這取決于您所說的“原子性”是什么意思。您確實需要這樣的情況,即當一個執行緒存盤時turn = i;,另一個執行緒加載turn只會讀取i或讀取j其他任何內容。在某些機器上,根據 and 的型別turn和值i,j您可能會被撕裂并加載完全不同的值。因此,無論您使用什么語言,都可能需要您以turn某種方式宣告為“原子”以避免這種情況。特別是在 C 中,如果turn沒有宣告std::atomic,那么任何并發的讀/寫訪問都是資料競爭,整個程式的行為變得未定義(這很糟糕)。
除了需要避免撕裂和資料競爭之外,Peterson 的演算法還需要嚴格的記憶體排序(順序一致性),除非特別要求,否則在許多系統/語言上不能保證這一點,同樣可能通過atomic以某種方式宣告變數。
確實,與更典型的鎖演算法不同,Peterson 不需要原子的讀-修改-寫,只需要原子的順序一致的加載和存盤。這正是使它成為有趣且聰明的演算法的原因。但是在復雜性和性能方面存在很大的權衡,特別是如果您需要兩個以上的執行緒,并且大多數現實生活中的系統確實具有相當有效的原子 RMW 指令,因此 Peterson 在實踐中很少使用。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/521474.html
