天天看點

golang 系列:sync.Once 講解

sync.Once 介紹

之前提到過 Go 的并發輔助對象:

WaitGroup

。同樣的, sync.Once 也是 Go 官方的一并發輔助對象,它能夠讓函數方法隻執行一次,達到類似 init 函數的效果。我們來看看它的簡單用法:

func main() {
    var once sync.Once
    onceFunc := func() {
        fmt.Println("Only once")
    }

    for i := 0; i < 10; i++ {
        once.Do(onceFunc)
    }
}           

這裡執行後我們将隻看到一次 Only once 的列印資訊,這就是 sync.Once 的一次性效果。

sync.Once 源碼

我們來看下 sync.Once 的源碼:

type Once struct {
    done uint32
    m    Mutex
}

func (o *Once) Do(f func()) {
    // 原子加載辨別值,判斷是否已被執行過
    if atomic.LoadUint32(&o.done) == 0 {
        o.doSlow(f)
    }
}

func (o *Once) doSlow(f func()) { // 還沒執行過函數
    o.m.Lock()
    defer o.m.Unlock()
    if o.done == 0 { // 再次判斷下是否已被執行過函數
        defer atomic.StoreUint32(&o.done, 1) // 原子操作:修改辨別值
        f() // 執行函數
    }
}           

從上面可以分析出,sync.Once 是通過對一個辨別值,原子性的修改和加載,來減少鎖競争的。