直接回答:Go 内存模型规定:没有同步关系的并发读写是数据竞争,行为未定义。happens-before 是事件间的偏序:同一 goroutine 内程序序即 happens-before;跨 goroutine 只有特定同步点能建立。主要同步原语:channel 发送 happens-before 对应接收完成(无缓冲的直接同步,缓冲的第 k 次接收先于第 k+cap 次发送完成);close(ch) happens-before 收到零值的接收;sync.Mutex 的 Unlock happens-before 下一次 Lock;sync/atomic 操作自 1.19 起正式提供顺序一致语义;sync.Once 的 Do 返回 happens-before 之后所有 Do 返回。

展开解析:经典错误模式是"标志位轮询":一个 goroutine 写 done = true,另一个 for !done {}——没有同步边,编译器和 CPU 都可能重排,看到 done 为真也不保证看到 done 之前写的数据。修法:done 用 channel 关闭广播、atomic.Bool、或 mutex 保护。初始化场景:var x = setup() 包级初始化有明确顺序;go 语句 happens-before goroutine 开始执行,但 goroutine 内写入对启动方不可见除非显式同步。实践原则:把 happens-before 当 API 契约的一部分来设计——共享数据要么消息传递(channel),要么锁保护,别依赖"看起来能工作"的时序巧合,race detector 跑全测试是最低要求。

追问方向:为什么 atomic 的 Load/Store 在 1.19 前文档不承认建立同步?sync.WaitGroup 建立什么顺序?race detector 的实现原理?

(约 460 字)