Go併發編程(八) 深刻理解 sync.Once

在上一篇文章《Week03: Go 併發編程(七) 深刻理解 errgroup》當中看 errgourp  源碼的時候咱們發現最後返回 err  是經過 once 來只保證返回一個非 nil 的值的,本文就來看一下 Once 的使用與實現編程

案例

once 的使用很簡單c#

`func main() {`
 `var (`
 `o  sync.Once`
 `wg sync.WaitGroup`
 `)`
 `for i := 0; i < 10; i++ {`
 `wg.Add(1)`
 `go func(i int) {`
 `defer wg.Done()`
 `o.Do(func() {`
 `fmt.Println("once", i)`
 `})`
 `}(i)`
 `}`
 `wg.Wait()`
`}`

輸出併發

`❯ go run ./main.go`
`once 9`

源碼分析

`type Once struct {`
 `done uint32`
 `m    Mutex`
`}`

done 用於斷定函數是否執行,若是不爲 0 會直接返回函數

`func (o *Once) Do(f func()) {`
 `// Note: Here is an incorrect implementation of Do:`
 `//`
 `// if atomic.CompareAndSwapUint32(&o.done, 0, 1) {`
 `//  f()`
 `// }`
 `//`
 `// Do guarantees that when it returns, f has finished.`
 `// This implementation would not implement that guarantee:`
 `// given two simultaneous calls, the winner of the cas would`
 `// call f, and the second would return immediately, without`
 `// waiting for the first's call to f to complete.`
 `// This is why the slow path falls back to a mutex, and why`
 `// the atomic.StoreUint32 must be delayed until after f returns.`
 `if atomic.LoadUint32(&o.done) == 0 {`
 `// Outlined slow-path to allow inlining of the fast-path.`
 `o.doSlow(f)`
 `}`
`}`

看 go 的源碼真的能夠學到不少東西,在這裏還給出了很容易犯錯的一種實現源碼分析

`if atomic.CompareAndSwapUint32(&o.done, 0, 1) {`
 `f()`
`}`

若是這麼實現最大的問題是,若是併發調用,一個 goroutine 執行,另一個不會等正在執行的這個成功以後返回,而是直接就返回了,這就不能保證傳入的方法必定會先執行一次了 因此回頭看官方的實現ui

`if atomic.LoadUint32(&o.done) == 0 {`
 `// Outlined slow-path to allow inlining of the fast-path.`
 `o.doSlow(f)`
`}`

會先判斷 done 是否爲 0,若是不爲 0 說明還沒執行過,就進入 doSlowatom

`func (o *Once) doSlow(f func()) {`
 `o.m.Lock()`
 `defer o.m.Unlock()`
 `if o.done == 0 {`
 `defer atomic.StoreUint32(&o.done, 1)`
 `f()`
 `}`
`}`

doSlow  當中使用了互斥鎖來保證只會執行一次spa

總結

  • Once 保證了傳入的函數只會執行一次,這經常使用在單例模式,配置文件加載,初始化這些場景下
  • 可是須要注意。Once 是不能複用的,只要執行過了,再傳入其餘的方法也不會再執行了
  • 而且 Once.Do 在執行的過程當中若是 f 出現 panic,後面也不會再執行了

參考文獻

  1. https://pkg.go.dev/sync#Once
  2. 6.2 同步原語與鎖

mohuishou

mohuishoucode

lailin.xyz 的博客帳號,關注但不限於 Go 開發,雲原生,K8s等圖片

36篇原創內容

公衆號

👆 關注我,_一塊兒在知識的海洋遨遊_

轉發,點贊,在看就是對我最大的鼓勵

圖片

點擊「閱讀原文」查看參考文獻等信息

相關文章
相關標籤/搜索