sync.Once源码分析
sync.Once是借助原子变量和锁来实现函数只执行一次的功能.
通过上锁, 避免首次并发竞争, 以后是通过atomic来cas获取变量状态来判断是否已执行, 提高效率
由于首次doSlow会上锁, 因此首次并发情况下, 会出现其他goroutine等待函数执行完并设置了done标志, 这里会有一个延时等待,
但是结合Once的使用, 一般是作为初始化时使用, 并发基本没有或者一般很低, 所以性能是没问题的.
```go
package sync
import (
"sync/atomic"
)
type Once struct {
// done 作为原子变量, cas方式标记已调用
done uint32
m Mutex
}
func (o *Once) Do(f func()) {
//两个goroutine同时调用, 其中一个goroutine会原子等待另外一个goroutine的结束
//因此并发调用Once时, 注意因并发而发生延迟问题
if atomic.LoadUint32(&o.done) == 0 {
o.doSlow(f)
}
}
//doSlow 可以看到doSlow 会先上锁, 然后执行完f()才会cas设置原子变量
//所以这里如果是并发调用once, 会出现因上锁又等待done为非0的情况
func (o *Once) doSlow(f func()) {
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
//cas设置done变量
defer atomic.StoreUint32(&o.done, 1)
f()
}
}
```