首頁 > 後端開發 > Golang > 為什麼「sync.Once」使用「atomic.StoreUint32」而不是「done」標誌的常規分配?

為什麼「sync.Once」使用「atomic.StoreUint32」而不是「done」標誌的常規分配?

Barbara Streisand
發布: 2024-10-31 05:40:01
原創
786 人瀏覽過

Why does `sync.Once` use `atomic.StoreUint32` instead of a regular assignment for the `done` flag?

AtomicStoreUint32 與Sync.Once 中的賦值

在探索Go 的sync.Once 類型的原始碼的問題atomic.StoreUint32 與設定完成標誌的常規分配。

不正確的實作:

原始原始碼包含不正確的實作:

<code class="go">func (o *Once) Do(f func()) {
    if atomic.CompareAndSwapUint32(&o.done, 0, 1) {
        f()
    }
}</code>
登入後複製

此實作無法保證函數回傳時f 已完成。同時呼叫可能會導致獲勝者執行 f,而第二個呼叫者立即返回(假設第一個呼叫已完成),但情況可能並非如此。

正確實作:

為了修正這個問題,目前的實作將atomic.StoreUint32與互斥體結合使用:

<code class="go">func (o *Once) Do(f func()) {
    if atomic.LoadUint32(&o.done) == 0 {
        o.doSlow(f)
    }
}</code>
登入後複製
<code class="go">func (o *Once) doSlow(f func()) {
    o.m.Lock()
    defer o.m.Unlock()
    if o.done == 0 {
        defer atomic.StoreUint32(&o.done, 1)
        f()
    }
}</code>
登入後複製

為什麼要使用AtomicStoreUint32?

原子的使用.StoreUint32 對於確保 f 完成後其他 goroutine 可以觀察到 o.done 的變化是必要的。雖然原始分配在某些架構上可能是原子的,但 Go 的記憶體模型需要使用原子包來確保所有支援的架構中的原子操作。

存取完成標誌:

目標是確保在互斥體之外對完成標誌的存取是安全的。因此,使用原子操作而不是使用互斥鎖進行鎖定。這種最佳化提高了快速路徑的效率,使sync.Once能夠部署在高流量的場景中。

doSlow的互斥體:

doSlow中的互斥體確保在 o.done 設定之前只有一個呼叫者執行 f。 atomic.StoreUint32 用於寫入標誌,因為它可能與互斥量臨界區以外的atomic.LoadUint32同時發生。

並發寫入與讀取:

直接讀取由於互斥保護,doSlow 中的 o.done 是安全的。此外,同時讀取 o.done 和atomic.LoadUint32 是安全的,因為這兩個操作都只涉及讀取。

以上是為什麼「sync.Once」使用「atomic.StoreUint32」而不是「done」標誌的常規分配?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板