深入浅出 Go - sync.Once 源码分析
sync.Once
是 Go 标准库提供的函数,可以用于实现单例模式,确保回调函数只执行一次,那么它是怎么实现的呢?
快速入门
首先来了解下如何使用 sync.Once
,它的使用方法很简单,如下
复制代码
可以看到只打印了一次输出
源码阅读
那么接下来我们通过源码来了解下 sync.Once
是如何实现的
复制代码
sync.Once
的源码实现非常简单,采用的是双重检测锁机制 (Double-checked Locking),是并发场景下懒汉式单例模式的一种实现方式
首先判断 done 是否等于 0,等于 0 则表示回调函数还未被执行
加锁,确保并发安全
在执行函数前,二次确认 done 是否等于 0,等于 0 则执行
将 done 置 1,同时释放锁
疑问一: 为什么不使用乐观锁 CAS
不使用乐观锁 CAS 的原因,这个在 sync.Once
的源码注释中已经明确说明了
复制代码
简单的来说就是 f()
的执行结果最终可能是不成功的,所以你会看到现在采用的是双重检测锁机制来实现,同时需要等 f()
执行完成才修改 done 值
疑问二: 为什么读取 done 值的方式没有统一
比较 done 是否等于 0,为什么有的地方用的是 atomic.LoadUint32
,有的地方用的却是 o.done
。主要原因是 atomic.LoadUint32
可以保证原子读取到 done 值,是并发安全的,而在 doSlow 中,已经加锁了,那么临界区就是并发安全的,使用 o.done
就可以来读取值就可以了
版权声明: 本文为 InfoQ 作者【哈希说】的原创文章。
原文链接:【http://xie.infoq.cn/article/3002c93dbec6813b347f969cb】。文章转载请联系作者。
评论