源码分析 sync.Once
源码
func (o *Once) Do(f func()) {
if atomic.LoadUint32(&o.done) == 1 {
return
}
// Slow-path.
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
defer atomic.StoreUint32(&o.done, 1)
f()
}
}
疑问
- 为什么要把原子操作和锁一起用?
- 我没能理解为什么要使用原子操作,试想如果o.Do已经成功执行过一次的情况,此时o.done的值为1,如果同时有100个线程调用o.Do,那么由于 atomic.LoadUint32(&o.done)是原子的,所以这一百个线程其实是排队依次读取这个值的,影响了并发性能;如果不用原子操作是不是会更好,这一百个线程就可以同时读取o.done的值;由于有锁,所以对o.done的赋值操作是单线程的,也没必要使用原子操作;修改后代码如下:
func (o *Once) Do(f func()) { if o.done == 1 { return } // Slow-path. o.m.Lock() defer o.m.Unlock() if o.done == 0 { f() o.done = 1 } }