在Go的sync.Once中正確使用原子操作
在Go的sync.Once實現的背景下,理解設定完成標誌時正常賦值和atomic.StoreUint32操作之間的差異。
不正確實現
最初,once.go 中的Do 函數使用了以下方法:
if atomic.CompareAndSwapUint32(&o.done, 0, 1) { f() }
此實作無法保證在 Do 返回時 f 的執行完成。對 Do 的兩個並發呼叫可能會導致第一個呼叫成功呼叫 f,而第二個呼叫過早返回,認為 f 已完成,即使它尚未完成。
原子儲存操作
為了解決這個問題,Go 採用了atomic.StoreUint32 操作。與普通賦值不同,atomic.StoreUint32 確保更新後的done標誌對其他goroutine的可見度。
記憶體模型注意事項
sync.Once中原子操作的使用是主要不受底層機器的記憶體模型的影響。 Go 的記憶體模型充當統一的抽象,確保不同硬體平台之間的行為一致,無論其特定的記憶體模型如何。
優化的快速路徑
要優化效能,請同步.Once 對於已設定完成標誌的常見場景採用快速路徑。此快速路徑利用atomic.LoadUint32 來檢查完成標誌而不取得互斥體。如果設定了該標誌,則函數立即返回。
具有互斥鎖和原子儲存的慢速路徑
當快速路徑失敗時(即,最初未設定完成),進入慢速路徑。取得互斥體以確保只有一個呼叫者可以繼續執行 f。 f完成後,使用atomic.StoreUint32設定done標誌,使其對其他goroutine可見。
並發讀取
即使設定了done標誌從原子角度來說,它並不保證並發讀取的安全。讀取受保護臨界區域以外的標誌需要使用atomic.LoadUint32。然而,由於互斥體提供了互斥,直接讀取臨界區是安全的。
總而言之,Go 的sync.Once 利用atomic.StoreUint32 來確保done 標誌的修改一致且可見,無論底層記憶體 モデル 並避免資料競爭。原子操作和互斥體的組合提供了性能最佳化和正確性保證。
免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。
Copyright© 2022 湘ICP备2022001581号-3