Go的内存模型定义了在并发环境下对一个变量的读操作能够看到哪些写操作。理解这些规则是写出正确并发程序的前提。一、happens-before关系Go的内存模型基于happens-before关系定义了操作之间的顺序约束。如果操作A happens-before操作B那么A的结果对B可见。单线程内的顺序在同一个goroutine中代码的执行顺序就是happens-before顺序。x 1; y 2中x 1happens-beforey 2。初始化顺序包级别的init函数在main之前执行import的包初始化在依赖包之前完成。goroutine创建go func()调用happens-before新goroutine的执行。启动goroutine之前写入的变量在goroutine内部可见。goroutine销毁goroutine的结束不保证happens-before任何事件。如果需要等待goroutine完成必须使用同步机制。二、同步原语的happens-before规则MutexUnlockhappens-before后续的Lock。锁释放时写入的变量在锁被重新获取后可见。RWMutexRUnlockhappens-before后续的RLock。Unlockhappens-before后续的RLock和Lock。WaitGroupAdd在Wait之前调用Donehappens-beforeWait返回。OnceDo中的函数执行完成 happens-beforeDo返回。多个goroutine并发调用Do时只有一个执行函数其他等待函数完成。三、Channel的同步语义无缓冲channel的同步性向无缓冲channel发送值 happens-before 从该channel接收值。这保证了发送方在发送之前写入的数据在接收方可见。有缓冲channel向缓冲channel发送值 happens-before 从该channel接收值仅当缓冲区非空时。如果缓冲区满发送方阻塞接收方先执行此时的happens-before关系反转。关闭channel关闭channel happens-before 从该channel接收到零值。这保证了在关闭channel之前写入的数据在接收方看到零值时已经可见。四、原子操作的可见性sync/atomic包提供的原子操作是低级的同步原语。原子操作本身保证原子性和可见性但不提供happens-before关系除非配合sync/atomic的内存顺序参数。atomic操作的主要用途是构建无锁数据结构和计数器不替代Mutex。govar counter int64 atomic.AddInt64(counter, 1) v : atomic.LoadInt64(counter)原子操作的开销低于Mutex但使用不当会导致难以调试的bug。五、数据竞争与检测数据竞争发生在两个或多个goroutine并发访问同一内存位置且至少有一个是写操作且没有同步机制。bashgo build -race go test -race-race标志启用数据竞争检测在运行时记录内存访问检测到竞争时输出报告。竞争检测有运行时开销通常增加10倍执行时间但在开发和测试阶段是必要的工具。六、小结Go的内存模型建立在happens-before关系之上。同步原语Mutex、WaitGroup、Once提供了显式的同步点channel提供了通信同步原子操作提供了底层控制。理解这些规则是正确使用并发机制的前提。