自旋鎖與互斥鎖的差異:等待方式、CPU 使用、持有時間、核心中的用法比較
自旋鎖與互斥鎖都是同一時間只讓 1 個執行緒進入臨界區段的鎖,但等待鎖釋放的方式不同。自旋鎖以在迴圈中持續檢查鎖變數的忙碌等待方式等待,因此等待期間也持續占用 CPU,但鎖一釋放就能立即取得。互斥鎖讓等待的執行緒進入睡眠並讓出 CPU,在鎖釋放時由 OS 喚醒。持有時間極短,或在無法睡眠的中斷處理常式中使用自旋鎖;持有時間長,或持有期間可能睡眠時使用互斥鎖。
| 項目 | 自旋鎖 | 互斥鎖 |
|---|---|---|
| 等待方式 | 在迴圈中持續檢查變數,直到鎖釋放(忙碌等待) | 若未釋放就睡眠等待,釋放時被喚醒 |
| 等待期間的 CPU | 持續占用 | 讓出。期間其他執行緒可以使用 CPU |
| 等待的開銷 | 沒有內文切換,鎖一釋放就能立即取得 | 睡眠與喚醒時,需要系統呼叫與內文切換的開銷 |
| 適合的持有時間 | 極短的時間(比內文切換所需的時間更短) | 長時間。持有期間可能睡眠時,例如等待 I/O |
| 持有期間睡眠 | 不可以。等待的一方會持續占用 CPU | 可以 |
| 只有 1 個核心時 | 等待期間持有鎖的執行緒無法執行,自旋也不會讓鎖釋放。在核心中會停止搶占後使用 | 可以正常使用 |
| 在核心中的用法 | 在中斷處理常式這類無法睡眠的場合(中斷內文)也能使用。與中斷共用資料時,先禁止中斷再取得 | 可能會睡眠,因此只能在行程內文中使用,無法在中斷處理常式中使用 |
| 優先權反轉 | 持有鎖的執行緒被暫停時,等待的一方會持續自旋。核心中以在持有期間禁止搶占來防止 | 可用具有優先權繼承的互斥鎖因應 |
| 實作範例 | 以原子指令(test-and-set 或 compare-and-swap)改寫變數。平時以一般讀取等待的 test-and-test-and-set 可減少核心之間的通訊 | Linux 的 futex 在沒有競爭時只以使用者空間的原子指令上鎖,只在發生競爭時才請核心讓執行緒睡眠。也有先自旋一下再睡眠的實作 |
數值為具代表性的大致參考值,會因世代或產品而異。