深刻源碼分析golang之sync.Once

圖片

什麼是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}`

掃碼關注

獲取更多幹貨內容

圖片

相關文章
相關標籤/搜索