直接回答:Go 内存模型是语言级契约,规定跨 goroutine 的读写何时可见、顺序何时确定。核心是 happens-before:A happens-before B 则 A 的效果对 B 可见。单 goroutine 内程序顺序天然成立;跨 goroutine 必须靠同步原语建立,否则编译器和 CPU 都可能重排、缓存延迟传播——无同步的共享读写就是数据竞争,行为未定义。

建立 happens-before 的手段:channel 发送 happens-before 对应接收完成;Mutex 第 n 次 Unlock happens-before 第 n+1 次 Lock 返回;atomic 写与读到该值的读之间有同步关系;Done happens-before Wait 返回。经典错误:

go func() { data = "x"; done = true }() // 无同步
for !done {} // 可能等不到,或读到空 data

正确做法用 channel 通知或 atomic.Bool。提醒:-race 只抓测试覆盖到的竞争;可见性 Bug 与硬件、编译器版本强相关,别依赖「测试时能看到」。

追问方向:双重检查锁定在 Go 里为什么仍需 atomic?缓冲 channel 容量影响同步语义吗?(约 591 字)