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 是通過對一個辨別值,原子性的修改和加載,來減少鎖競争的。