go sync.Once源码分析
适用场景
服务启动时读取全局配置。
单个函数流程里面只调用一次。
源码
双重检查done值是0后,加锁执行指定函数并把done值改成1。
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()
}
}
源码使用Mutex的原因
package main
import "sync/atomic"
type Once struct {
done uint32
}
func (o *Once) Do(f func()) {
if !atomic.CompareAndSwapUint32(&o.done, 0, 1) {
return
}
f()
}
相比于源码,直接使用cas的问题是,第1个goroutine在成功执行cas后没有执行完指定函数,其他goroutine因cas失败而返回后,使用未初始化的资源时可能会panic。
第1个goroutine结束前,阻塞住其他goroutine,确保所有goroutine可以正常访问资源。