同步

公平與優先權反轉

公平對任何等待隊伍問一個簡單問題:每個人最終都會被服務到嗎,還是有人可能永遠被跳過?公平的鎖或排程器保證每個等待的執行緒最終都輪得到——沒有執行緒被餓死。不公平但快的,則可能讓一個剛釋放鎖的執行緒立刻重新搶到它,這很有效率,卻可能把一個耐心的等待者晾著。多數互斥鎖預設並不嚴格公平,它們以「保證輪流」換取速度;當你真的需要順序時,就去要一把公平(FIFO)鎖。

優先權反轉是一種特定、惡名昭彰的公平失效,發生在執行緒帶有優先權時。想像三個執行緒:高、中、低優先權。低優先權執行緒拿了一把鎖。高優先權執行緒接著想要同一把鎖,於是它必須等低優先權執行緒釋放——這已經是輕微的反轉,因為高在等低。災難來自:若有一個(不需要這把鎖的)中優先權執行緒可執行,排程器會偏好中而非低,於是持鎖的低優先權執行緒永遠跑不到、永遠不釋放鎖,而高優先權執行緒就被一個跟它毫不相干的中優先權執行緒無限期地阻塞。優先權實質上被反轉了:中勝過高。著名的真實案例是一九九七年的火星探路者著陸器,它在太空中不斷重置自己,直到工程師診斷出正是這個問題。

標準解藥是優先權繼承:當高優先權執行緒等在一把由低優先權執行緒持有的鎖上時,系統暫時把持鎖者提升到高優先權,好讓它能跑、做完它的臨界區間、迅速釋放鎖——之後再降回去。(一個更簡單的變體,優先權天花板,把任何持鎖者提升到該鎖預定的天花板優先權。)這些屬於即時系統,那裡錯過截止時間是失敗,而不只是變慢。對一般應用程式碼,教訓較謙卑卻真實:鎖會與排程器互動、「公平」與「快」彼此拉扯,而一個持著熱門鎖的低優先權執行緒可能傷害整個系統。

低優先權 L 持著鎖 m;高優先權 H 等著 m;中優先權 M(不需要 m)持續跑並搶佔 L。L 永遠不釋放 m,於是 H 被 M 阻塞——這就是優先權反轉。優先權繼承暫時把 L 提升到 H 的優先權,好讓它釋放 m。

中優先權執行緒可透過低執行緒持有的鎖餓死高執行緒;繼承能修正它。

多數通用互斥鎖預設並不公平——除非文件明確承諾,否則別假設等待者是 FIFO 順序。優先權反轉是即時系統的課題;在沒有執行緒優先權的一般應用程式碼裡通常不會出現,但不公平的鎖仍可能造成一般的飢餓。

又称
priority inversionfairnessunbounded priority inversion優先權倒置公平性