多核心、一致性與執行緒層級平行

自旋鎖(spinlock)

想像一間沒有預約系統的單人廁所。當你發現門鎖著,你不會離開——你就站在那裡不停地搖把手,一次又一次,直到它打開,然後你衝進去、把門反鎖。你浪費時間拉把手,但它一空出來你就是第一個進去。自旋鎖(spinlock)就是這種鎖:想要鎖的執行緒在一個緊湊的迴圈裡不停測試它——自旋——燒掉 CPU 週期直到鎖空出來,而不是去睡覺。

在底層,自旋鎖是一個共享旗標(0 = 空閒,1 = 被持有)加上一個原子讀-改-寫。要取得鎖,執行緒迴圈地試圖原子地把旗標從 0 翻成 1——用測試並設定(test-and-set),或比較並交換,或 LL/SC。當別的執行緒持有它時,每次嘗試都失敗,執行緒就原地重試。當持有者把旗標設回 0 來釋放時,某個自旋執行緒的原子翻轉終於成功,它就進入臨界區。一個好的自旋鎖會先在一個普通的讀取上自旋(一個不產生一致性流量的唯讀迴圈),只在鎖看起來空閒時才發出那個昂貴的原子操作——這就是「測試-再測試並設定」技巧——以免猛敲那條快取列。

當預期的等待非常短、而且你有一個多餘的核心可以自旋時,自旋鎖是對的工具——鎖只被持有幾條指令,所以自旋一下子,比把執行緒弄睡再喚醒的開銷便宜。對於漫長或競爭激烈的等待,它就是錯的工具:一個自旋的執行緒做零有用功,卻燒著電,更糟的是產生一致性流量,可能拖慢它正在等的那個持有者。自旋也有優先權反轉的風險,並在單核心上是災難(你把整個時間片自旋耗光,去等一個根本無法執行的持有者)。對於較長的等待,一個會讓執行緒睡著的阻塞互斥鎖(mutex)更好。

測試-再測試並設定的取得:while (true) { while (lock == 1) ; /* 唯讀自旋直到它看起來空閒 */ if (CAS(&lock, 0, 1) == 成功) break; }。釋放:lock = 0。唯讀的內層自旋讓快取列保持 Shared 而安靜;只有那短暫的 CAS 嘗試才觸發一致性流量,而且只在鎖看起來空閒時。

先在唯讀檢查上自旋,能讓鎖那條列保持安靜;昂貴的原子操作只在鎖看起來空閒時才發出。

自旋鎖適合多核心上非常短、低競爭的臨界區。對漫長或競爭激烈的等待,它浪費 CPU 與電力、又堵塞一致性流量;此時會睡著的互斥鎖更好,而在單核心上自旋可能讓進度死鎖。

又稱
spin lockbusy-wait lock忙等鎖旋轉鎖