分析如下

type Once struct {
    // done indicates whether the action has been performed.
    // It is first in the struct because it is used in the hot path.
    // The hot path is inlined at every call site.
    // Placing done first allows more compact instructions on some architectures (amd64/386),
    // and fewer instructions (to calculate offset) on other architectures.
    done uint32
    m    Mutex
}


func (o *Once) Do(f func()) {
    // 先做一次快速判断,如果已经是1,则直接返回
    // 否则doSlow
    if atomic.LoadUint32(&o.done) == 0 {
        // Outlined slow-path to allow inlining of the fast-path.
        o.doSlow(f)
    }
}


// 对done的修改需要互斥
// 因为Once常用于加载配置,然后再执行业务
// 如果直接对比done,不为零就跳过f,那么会导致配置未加载就执行业务
// 所以需要其余goroutine也等待一下,并且f执行完才设置done
//
// Note: Here is an incorrect implementation of Do:
//
//    if atomic.CompareAndSwapUint32(&o.done, 0, 1) {
//        f()
//    }
//
// Do guarantees that when it returns, f has finished.
// This implementation would not implement that guarantee:
// given two simultaneous calls, the winner of the cas would
// call f, and the second would return immediately, without
// waiting for the first's call to f to complete.
// This is why the slow path falls back to a mutex, and why
// the atomic.StoreUint32 must be delayed until after f returns.
func (o *Once) doSlow(f func()) {
    o.m.Lock()
    defer o.m.Unlock()
    if o.done == 0 {
        defer atomic.StoreUint32(&o.done, 1)
        f()
    }
}