」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 為什麼 Go 的 `sync.Once` 使用 `atomic.StoreUint32` 而不是普通的賦值來設定 `done` 標誌?

為什麼 Go 的 `sync.Once` 使用 `atomic.StoreUint32` 而不是普通的賦值來設定 `done` 標誌?

發佈於2024-11-25
瀏覽:497

Why does Go\'s `sync.Once` use `atomic.StoreUint32` instead of normal assignment to set the `done` flag?

在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