概述
Once可以让一个动过只发生一次,不管该动作在多少个协程中执行。这种操作可以用于在多线程编程中只允许运行一次的代码块,或初始化代码块中。
函数介绍
func (o *Once) Do(f func())
Once结构的实现
// Once is an object that will perform exactly one action.
type Once struct {
m Mutex
done uint32
}
// Do函数的实现源码
func (o *Once) Do(f func()) {
// 若已经为1了,说明已经执行了一次,直接返回,不在执行
if atomic.LoadUint32(&o.done) == 1 {
return
}
// Slow-path.
// 加锁,并设置为1
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
defer atomic.StoreUint32(&o.done, 1)
f()
}
}
Once的使用
func TestSyncOnce() {
var count int
increment := func() {
count++
}
var once sync.Once
var increments sync.WaitGroup
increments.Add(100)
for i := 0; i < 100; i++ {
go func() {
defer increments.Done()
once.Do(increment)
}()
}
increments.Wait()
fmt.Printf("Count is %d\n", count)
}
输出如下:
Count is 1
说明:从Once的实现可以看出,当执行一次后,再执行时将不会在执行了。
由于Once而产生的死锁
var onceA, onceB sync.Once
var initB func()
initA := func() { onceB.Do(initB) }
initB = func() { onceA.Do(initA) } [1]
onceA.Do(initA) [2]
死锁原因分析:
直到[2]返回后[1]才会被执行,若从加锁的序列来看,如下:
lockA
locakB
lockA // 尝试加锁,但不会成功因此阻塞
总结
sync包的Once类型,只会执行一次,这为在多线程的情况下进行初始化,或需要执行一次的工作提供了遍历。但在使用时需要注意由于Once的相互嵌套而引起的死锁问题。