源码分析 sync.Once

源码分析 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
	}
}

共 1 个回复


me@bzza.com

在协程下,你是对的,官方源码也是对的,没区别!

# 0