为什么Go中支持atomic.Load和atomic.Store?

Mar*_*ity 1 atomic go

我认为atomic.Load(addr)应该等于*addr并且atomic.Store(addr, newval)应该等于*addr = newval。那么为什么这样做(使用*addror *addr = newval)不是原子操作呢?我的意思是它们最终会被解释为只是一条 cpu 指令(这是原子的)?

Bur*_*dar 5

因为顺序保证和内存操作可见性。例如:

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内存模型:

https://go.dev/ref/mem