Go 的sync.Once 中原子操作的正確使用
在Go 的sync.Once 實現的上下文中,了解以下內容至關重要:設定done標誌時普通賦值與atomic.StoreUint32操作之間的差異。
不正確的實作
最初,once.go中的Do函數使用了以下方法:
if atomic.CompareAndSwapUint32(&o.done, 0, 1) { f() }
此實作無法保證在Do 回傳時f 的執行完成。對 Do 的兩個並發呼叫可能會導致第一個呼叫成功呼叫 f,而第二個呼叫過早返回,認為 f 已完成,即使它尚未完成。
原子儲存操作
為了解決這個問題,Go 使用了atomic.StoreUint32 操作。與普通賦值不同,atomic.StoreUint32 確保更新後的完成標誌對其他 goroutine 的可見度。
記憶體模型注意事項
sync.Once 中原子操作的使用是主要不受底層機器的記憶體模型的影響。 Go 的記憶體模型充當統一的抽象,確保不同硬體平台之間的行為一致,無論其特定的記憶體模型為何。
最佳化的快速路徑
要最佳化效能,請同步.Once 對於已設定完成標誌的常見場景採用快速路徑。此快速路徑利用atomic.LoadUint32 來檢查完成標誌而不取得互斥體。如果設定了該標誌,則函數立即傳回。
具有互斥鎖和原子儲存的慢速路徑
當快速路徑失敗時(即最初未設定完成),進入慢速路徑。取得互斥體以確保只有一個呼叫者可以繼續執行 f。 f完成後,使用atomic.StoreUint32設定done標誌,使其對其他goroutines可見。
並發讀取
即使設定了done標誌從原子角度來說,它並不保證並發讀取的安全。讀取受保護臨界區域以外的標誌需要使用atomic.LoadUint32。然而,由於互斥體提供了互斥,直接讀取臨界區是安全的。
綜上所述,Go 的sync.Once 利用atomic.StoreUint32 來確保done 標誌的修改一致且可見,無論底層記憶體 モデル 並避免資料競爭。原子操作和互斥體的結合提供了性能最佳化和正確性保證。
以上是為什麼 Go 的 `sync.Once` 使用 `atomic.StoreUint32` 而不是普通的賦值來設定 `done` 標誌?的詳細內容。更多資訊請關注PHP中文網其他相關文章!