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

// 为什么不直接用 CompareAndSwap,而是使用 sync.Once ?

// 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() 执行完成。

// 为什么需要两次检查?
// - 第一次检查是性能优化(类似于乐观锁)。
// - 第二次检查是真正加锁,保证只有一个 goroutine 执行 f()。

// 应用场景:
// 1. 单例模式:确保全局对象只初始化一次。
// 2. 延迟资源初始化:比如项目 A 默认依赖数据库 A,
// 但某些非核心接口才依赖数据库 B,就可以对数据库 B 进行延迟加载,
// 这样既节省资源,也降低风险。

评论

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

回复

邮箱