golang sync.once 的底层实现以及应用场景

sync.once 

//为什么不使用这种写法
if done.CompareAndSwap(0,1) {
func()
}
sync.Once 的语义是:无论多少 goroutine 调用,f() 只执行一次,
而且一旦执行过(不管是否成功),后面都不会再执行。

如果直接用 CompareAndSwap

1. 问题的核心:异常/失败处理

第一个 goroutine 执行到 CAS 成功 → f() 开始执行。

如果 f() 执行过程中 panic 或出现错误,done 已经被设为 1

其他 goroutine 再调用 Do() 时,done==1 → 不会再执行 f()

这可能导致资源初始化失败后,永远无法重试。


2. 另一个问题:并发下 CAS 不能防止并发执行
假设两个 goroutine 同时执行:

Goroutine A 成功 CAS(0→1),开始执行 f()

Goroutine B 读取到 done==1 → 跳过执行(这里没问题)。

但问题在于,如果我们需要等待 f() 完成后其他 goroutine 才继续,CAS 方案做不到。
标准库的 sync.Once 用锁可以保证:其他 goroutine 会阻塞直到 f() 完成,然后再返回。 为什么两次检查呢,第一次检查是为了性能优化,类似于乐观锁。第二次才是真正上锁。 sync.once 应用场景 1.一种情况是为了实现单例模式为了实现全局对象。 2.第二种就是延迟资源初始化,比如项目A依托于数据库A。但是有个非核心接口,需要调用数据B ,这时候就可以对数据库B进行延迟加载。毕竟加载数据库一方面消耗资源和时间,另外可以降低风险。

评论

(= ̄ω ̄=)··· 暂无内容!

回复

邮箱