在开发时,我们经常碰到这样的应用场景:只需要执行或初始化一次,例如,单例模式或是系统开启时的初始化。一般的语言通用做法是利用互斥操作设置标志变量,通过判断标志变量的值决定是否执行相关代码;golang语言的实现非常简单,只需一行代码,如下:
import "sync"
var once sync.Once
func init() {
//初始化代码
}
once.Do(init) // 只需一行代码,结构清晰,一目了然
那么once.Do()这个方法是如何实现的呢?我们来解析golang实现的一次操作:
首先, “sync” 是一个引用的包,具体实现在该包中; Once是定义的一个结构体对象,如下:
type Once struct {
m Mutex
done uint32
}
看到这里,我们基本应该可以猜出once.Do()的实现思路,Once.m是一个互斥锁,在进行Do()操作时,进行互斥操作;Once.done是一个标志变量,表示是否进行过Do()的操作。Do()的具体实现如下:
func (o *Once) Do(f func()) {
if atomic.LoadUint32(&o.done) == 1 {
return
}
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
defer automic.StoreUint32(&o.done, 1)
f()
}
}
实现思路与我们的猜测基本一致,它引入了原子操作包 import "sync/atomic",通过automic.LoadUint32和automic.StoreUint32操作实现标志变量Once.done的载入和存储,这样更精细化的原子操作的实现能进一步提高系统处理的速度,减少协程对互斥锁不必要的等待。
有疑问加站长微信联系(非本文作者)