同步一次实现

G.y*_*ang -5 synchronization go

我在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()
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么不只使用一个uint32变量,然后对该变量执行CAS。它似乎更有效,并且不会导致死锁。

代码如下:

type Once uint32
func (o *Once) Do(f func()) {
    if atomic.CompareAndSwapUint32((*uint32)(o), 0, 1) {
        f()
    }
}
Run Code Online (Sandbox Code Playgroud)

icz*_*cza 11

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

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

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