Once用于只执行一次的场景,常用在懒汉型单例模式。
var client *Client
var clientOnce sync.Once
func NewClient() *Client{
clientOnce.Do(func(){
client = &Client{}
})
return client
}
Once源码解析
Once由done标志和m互斥锁组成。其中,done=0表示Once还没有被执行,done=1表示Once已经被执行。 m 互斥锁用于控制协程对f()方法的互斥访问。
type Once struct {
done uint32
m Mutex
}
Once采用Double-Check双检查机制实现。首先判断“Once是否被执行过”,没有执行过则进入doSlow()函数执行f方法。由于在Once初始化的时候,存在多个协程进入doSlow函数的情况;对此,采用Mutex互斥锁保证只有一个协程执行f方法,再次判断“Once是否被执行过”防止后面的协程再次执行f方法。
func (o *Once) Do(f func()) {
// 判断Once是否被执行过:
if atomic.LoadUint32(&o.done) == 0 {
// done==0: 没有被执行过,执行doSlow函数
o.doSlow(f)
}
// done==1: 已经执行过,直接退出函数
}
func (o *Once) doSlow(f func()) {
// 当多个协程执行Once.Do()时,会有多个协程进入这里;
// 因此,需要通过Mutex互斥锁控制协程对f()函数的访问,保证同一时刻只有一个协程进入
o.m.Lock()
defer o.m.Unlock()
// 进入临界区后,判断Once是否被执行过:
if o.done == 0 {
// done==0表示f函数没有被执行过
// 先执行f(),然后通过原子操作更新done标志位
defer atomic.StoreUint32(&o.done, 1)
f()
}
// done!=0表示f函数已经被执行
}