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中文網其他相關文章!