# Go sync.Once的三重门

## 一、为什么不能直接使用一个flag+原子操作简单实现？

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.

## 三、为什么乱序执行没影响？

Go的标准库的代码质量是非常高的，而且都经过大神的仔细review,所以这样设计肯定是有它的用处的。

On strongly ordered architectures like x86/amd64, acquire load and release store are just regular loads and stores. To make them atomic you need to ensure the memory is aligned to the operand size (automatic in Go), and that the compiler doesn't re-order them in incompatible ways, or optimize them away (e.g. reuse a value in a register instead of reading it from memory.)

Ian Lance Taylor曾经在论坛中说:

In C++ memory model terms I believe that the sync/atomic Load
operations are memory_order_acquire, and I believe that the
sync/atomic Store operations are memory_order_release. It's possible
that if we ever document it we will go for stronger memory ordering,
but I believe that these operations must at least carry those guarantees.

I'm somewhat less certain of the memory order guarantees of the Swap,
CompareAndSwap, and Add functions. I guess that Swap and
CompareAndSwap are probably at least memory_order_acq_rel, but Add may
be memory_order_relaxed.

rsc
2019年7月16日上午9:12:01

Although there's been no official resolution to the issue, I think the actual path forward is what I posted a while back: "Go's atomics guarantee sequential consistency among the atomic variables (behave like C/C++'s seqconst atomics), and that you shouldn't mix atomic and non-atomic accesses for a given memory word."