無鎖與等待無關程式設計

等待無關進展(wait-free progress)

無鎖保證群體總在前進,卻容許某個倒楣的執行緒永遠重試、每場競賽都輸。等待無關(wait-free)堵住了這個漏洞。它是現存最強的進展保證,並且對每一個執行緒指名做出承諾:你會完成,而且會在你自己有限的步數內完成,無論其他所有執行緒在做什麼。

精確地說:一個演算法是等待無關的,是指每個執行緒都在有限且有上界的步數內完成每次操作,與所有其他執行緒的速度、暫停、甚至永久停擺無關。沒有那種可能讓你被永遠擊敗的無上界重試迴圈。注意這嚴格強於無鎖:每個等待無關演算法自動也是無鎖的(若人人都會完成,當然就有人完成),但無鎖演算法未必是等待無關。在無鎖設計之上達成等待無關的經典做法是「協助」(helping):一個將要開始操作的執行緒,會先幫忙完成它所看見、其他執行緒正在進行中的操作,使得沒有執行緒會被丟下。

為什麼這不是處處的預設?因為等待無關演算法通常難設計得多,而且在常見的無競爭情況下往往較慢——那些用來框住最壞情況的記帳工作,會替每一次操作都加上額外負擔。所以等待無關保留給真正在乎硬性即時上界的場合:音訊回呼中的執行緒、高優先權的控制迴圈,或一個被飢餓的執行緒就無法接受的系統。對大多數程式碼來說,無鎖、甚至一個樸素的鎖,才是務實的選擇。

/* 等待無關的原子計數器:fetch_add 總在有界步數內回傳。 */ long next_id(void) { return atomic_fetch_add(&counter, 1); /* 沒有重試迴圈,沒有自旋 */ }

單一的 fetch_add 是等待無關的:沒有執行緒會被迫繞迴圈或等待,因此每次呼叫都在固定步數內完成。

每個等待無關演算法都是無鎖的,但反之不然。CAS 重試迴圈是無鎖、不是等待無關,因為一個執行緒可能每次重試都輸;等待無關禁止無上界的重試。

又称
wait-freedomper-thread progress guarantee等待無關性