go version 1.19.7
应用场景:
1、单例模式: 确保全局只有一个实例对象
2、延迟初始化:在程序运行过程中需要用到某个资源时,通过sync.once 动态初始化改资源
3、执行一次的操作: 只需要执行一次的配置加载、数据清理等操作
源码
type Once struct {
// 用于表示操作是否已经执行过
done uint32
// m 是一个互斥锁, 用于确保多个协程访问时,只有一个协程能执行操作
m Mutex
}
// 接收一个函数参数,
func (o *Once) Do(f func()) {
if atomic.LoadUint32(&o.done) == 0 {
// 原子获取 done 的值,判断 done 的值是否为 0,如果为 0 就调用 doSlow 方法,进行二次检查。
o.doSlow(f)
}
}
func (o *Once) doSlow(f func()) {
// 二次检查时,持有互斥锁,保证只有一个 goroutine 执行。
// 这一检查是为了确保在当前协程获取锁期间,其他协程没有执行过 f 函数。
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
// 二次检查,如果 done 的值仍为 0,则认为是第一次执行,执行参数 f,并将 done 的值设置为 1。
defer atomic.StoreUint32(&o.done, 1)
f()
}
}
注意事项
1、不可以重复调用DO 方法, 会导致死锁
func main() {
once := sync.Once{}
once.Do(func() {
once.Do(func() {
fmt.Println("init...")
})
})
}
2、当执行f 函数的过程中发生了error, 现有的 sync.once 无法感知的,