Go 内存模型:happens-before
C++ 有 std::atomic + 内存序(memory_order),Go 有更少的口子但边界更清楚——channel / Mutex / Once / atomic 各自建立 happens-before 一、简述 “变量加了锁就不会有竞态"这种直觉在并发世界里是危险的。真正需要回答的问题是:goroutine A 写了一个值,goroutine B 什么时候能确定读到它? Go 用一份官方文档——The Go Memory Model——精确规定了这些"什么时候”。它定义了 happens-before 关系:同步原语(channel、Mutex、WaitGroup、Once、atomic)在收发/加解锁/等待/原子操作之间建立序,从而保证内存可见性。理解它,你才知道为什么"不加同步 flag 读 bool"可能读到旧值,也才知道怎么写出不靠运气的并发程序。 核心要点:happens-before(HB)是"一个事件必须在另一个事件之前发生且其结果可见"的形式化保证。Go 的并发正确性全部建立在"你用了某个同步原语,它就给你对应的一条 HB 边"上。不用原语 = 没有边 = 编译器/硬件随便重排。 二、C++ 对照速查表 概念 C++ Go 关键差异 内存模型文档 [memory_order] 标准章节 The Go Memory Model(官方文档) C++ 需要你精确选 memory_order;Go 不给选项,只规定用了哪个同步原语获得哪条 HB 边 互斥锁 std::mutex 加锁/解锁 sync.Mutex Lock/Unlock C++ 还要额外配 atomic 才能安全读共享变量;Go 锁内一切可见 放松内存序 memory_order_relaxed 无对应概念 Go 只有两档:同步原语→对应 HB 保证,非同步→无保证 原子操作 std::atomic<T>(load/store/fetch等) sync/atomic Go 1.19 之前 atomic 只防撕裂不建 HB;1.19+ 视作同步点 初始化一次 std::once_flag sync.Once once.Do 完成后,所有 Do 返回点看到其写入 线程等待 std::thread::join sync.WaitGroup.Wait Wait 返回后看到各 Done 的写入 通信同步 mutex+cv 手写 channel 收发建立 HB,官方推荐优先 channel 主动读屏障 atomic_thread_fence 无直接对应 Go 用 atomic.Load/互斥代替 fence 悬而未决的"下次可见" 无 “稍后某个时刻可见"是合法但无保证 这是最常见的 bug 来源 数据竞争检测 TSan(第三方) go test -race 内建 干干净净的"竞态即报” 三、逐主题详解 3.1 你已经在 C++ 里见过的"序" C++ 的 memory_order 有三种强度:relaxed(无序)、acquire/release(只保证可见但不能越过)、seq_cst(全局一致)。Go 的模型把选择收敛成一条规则: ...
豫公网安备41108202000982号