同步一次实现

时间:2019-06-03 08:11:21

标签: go synchronization

我对Go 1.12中的 sync.Once() 有疑问。源代码如下:

// Because no call to Do returns until the one call to f returns, if f causes
// Do to be called, it will deadlock.

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()
    }
}

为什么不只使用uint32变量,而是对这个变量进行CAS。它似乎更有效,并且不会导致死锁。

类似的代码:

type Once uint32
func (o *Once) Do(f func()) {
    if atomic.CompareAndSwapUint32((*uint32)(o), 0, 1) {
        f()
    }
}

1 个答案:

答案 0 :(得分:10)

Once.Do()仅在执行一次f()之后才返回。这意味着,如果多个goroutine并发调用Once.Do(),则f()当然会执行一次,但是所有调用都会等到f()完成(它们会被阻止)。

您提出的解决方案没有这个非常重要的属性!您只能保证f()仅执行一次,但是即使同时从多个goroutine中调用,即使f()仍在运行,后续调用也会立即返回。

使用sync.Once时,我们依赖于此行为,我们依靠f()在调用Once.Do()之后完成,因此我们可以使用f()安全初始化的所有变量,而没有比赛条件。