我认为atomic.Load(addr)应该等于*addr并且atomic.Store(addr, newval)应该等于*addr = newval。那么为什么这样做(使用*addror *addr = newval)不是原子操作呢?我的意思是它们最终会被解释为只是一条 cpu 指令(这是原子的)?
因为顺序保证和内存操作可见性。例如:
y:=0
x:=0
x=1
y=1
Run Code Online (Sandbox Code Playgroud)
在上面的程序中,另一个 goroutine 可以看到 x 和 y 的 (0,0)、(0,1)、(1,0) 或 (1,1)。这是因为编译器对代码重新排序、编译器优化或由于硬件级别的内存操作重新排序。然而:
y:=0
x:=0
x:=1
atomic.StoreInt64(&y,1)
Run Code Online (Sandbox Code Playgroud)
如果另一个 goroutine 看到atomic.LoadInt64(&y)==1,则保证该 goroutine 看到 x=1。
另一个例子是忙等待。以下示例来自 go 内存模型:
var a string
var done bool
func setup() {
a = "hello, world"
done = true
}
func main() {
go setup()
for !done {
}
print(a)
}
Run Code Online (Sandbox Code Playgroud)
不保证该程序终止,因为不保证 for 循环main能够看到done=true赋值。该程序可能会无限期地运行,可能会打印空字符串,也可能会打印“hello,world”。
替换done=true为原子存储,并使用原子加载检查 for 循环可保证程序始终完成并打印“hello, world”。
关于这些的权威文档是go内存模型: