golang源码分析 sync.Once

crud-bird · · 993 次点击
你修改之后 o.done = 1是锁住的区域赋值的, if o.done==1没加锁读, 这里就有数据竞争的问题。除非用lock把你读的地方包裹下。好了,你现在得到的代码才是每次都能正确处理的实现。 对于这种代码,如果想优化,把锁的颗粒度变小,可以atomic把done的地方改改,最后发现,我c 这不就是标准库的写法吗。。。
#1
更多评论
这里用的双检查锁结构,if o.done==1这一行并发读是没问题的,不存在你所谓的数据竞争。
#2
jan-bar
想要拥有,必定付出。
defer atomic.StoreUint32(&o.done, 1)是在Do返回之后执行,这个应该很有必要吧。我的1.14.1源码注释有说这个问题吧 ```go func (o *Once) Do(f func()) { // Note: Here is an incorrect implementation of Do: // // if atomic.CompareAndSwapUint32(&o.done, 0, 1) { // f() // } // // Do guarantees that when it returns, f has finished. // This implementation would not implement that guarantee: // given two simultaneous calls, the winner of the cas would // call f, and the second would return immediately, without // waiting for the first's call to f to complete. // This is why the slow path falls back to a mutex, and why // the atomic.StoreUint32 must be delayed until after f returns. if atomic.LoadUint32(&o.done) == 0 { // Outlined slow-path to allow inlining of the fast-path. 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() } } ```
#3