什麼是sync.Once 設計模式
官方文檔對它的描述是:一個對象將徹底執行一次,不得複製。經常用來單例對象的初始化場景,或者併發訪問只須要初始化一次的共享資源。sync.Once只暴露了一個方法Do,能夠屢次調用,可是隻有第一次調用Do方法時f參數纔會執行,這裏的f是一個無參數無返回值的函數。下面來看下官方給出來的demo:併發
`1package main` `2` `3import (` `4 "fmt"` `5 "sync"` `6)` `7` `8func main() {` `9 var once sync.Once` `10 onceBody := func() {` `11 fmt.Println("Only once")` `12 }` `13 done := make(chan bool)` `14 for i := 0; i < 10; i++ {` `15 go func() {` `16 once.Do(onceBody)` `17 done <- true` `18 }()` `19 }` `20 for i := 0; i < 10; i++ {` `21 <-done` `22 }` `23}` `24// 結果只打印一次:only once`
源碼分析 函數
sync.Once的源代碼還不多的,直接在代碼裏面做分析源碼分析
`1package sync` `2` `3import (` `4 "sync/atomic"` `5)` `6` `7type Once struct {` `8 done uint32 // 初始值爲0表示還未執行過,1表示已經執行過` `9 m Mutex // m爲互斥鎖` `10}` `11` `12func (o *Once) Do(f func()) {` `13 // 判斷done是否爲0.若爲0,表示未執行過,調用doSlow方法` `14 if atomic.LoadUint32(&o.done) == 0 {` `15 o.doSlow(f)` `16 }` `17}` `18` `19func (o *Once) doSlow(f func()) {` `20 // 互斥鎖加鎖解鎖` `21 o.m.Lock()` `22 defer o.m.Unlock()` `23 // 加鎖判斷done是否爲0` `24 if o.done == 0 {` `25 // 執行完f()函數後,將done值設置爲1` `26 defer atomic.StoreUint32(&o.done, 1)` `27 f()` `28 }` `29}`
Do方法流程圖: ui
疑問1:爲何在do方法的時候沒有用到cas原子判斷? atom
在do方法源碼註釋中的時候有這麼一段話,說若是是使用 atomic.CompareAndSwapUint32(&o.done, 0, 1)此方法不會保證明現f函數調用完成。爲何會這麼說,同時進行兩次調用,cas的獲勝者將調用f函數,失敗者將當即返回,而無需等待第一個對f的調用完成。此時f函數還在執行過程當中,你就已經回覆了once.Do成功了,此時全局變量尚未建立出來,行爲是沒法定義的。那麼怎麼解決呢?有如下兩個思路:spa
1)熱路徑:用原子讀done的值,保證競態條件正確;設計
2)加鎖:既然不能用cas原子操做,那就用加鎖的方式來保證原子性,若是done==0,那麼走慢路徑,先加鎖,而後在執行f函數,最後將done設置爲1,並解鎖。 code
重點:千萬不要把同一sync.Once用在嵌套結構中,這樣很是容易造成死鎖!對象
`1func once() {` `2 once := &sync.Once{}` `3 once.Do(func() {` `4 once.Do(func() {` `5 fmt.Println("test nestedDo")` `6 })` `7 })` `8 //輸出:fatal error: all goroutines are asleep - deadlock!` `9}`
sync.Once實現單例模式
單例模式能夠說是設計模式裏面最簡單的了,Go能夠藉助sync.Once來實現,代碼以下:
`1package main` `2` `3import (` `4 "fmt"` `5 "sync"` `6)` `7` `8type Singleton struct{}` `9` `10var (` `11 singleton *Singleton` `12 once sync.Once` `13)` `14` `15func GetSingleton() *Singleton {` `16 once.Do(func() {` `17 fmt.Println("Create Obj")` `18 singleton = new(Singleton)` `19 })` `20 return singleton` `21}` `22` `23func main() {` `24 var wg sync.WaitGroup` `25 for i := 0; i < 5; i++ {` `26 wg.Add(1)` `27 go func() {` `28 obj := GetSingleton()` `29 fmt.Printf("%p\n", obj)` `30 wg.Done()` `31 }()` `32 }` `33 wg.Wait()` `34 // 只打印一次 Create Obj` `35}`
掃碼關注
獲取更多幹貨內容