绝大多数情况下必须用 memory_order_seq_cst,它最安全、最符合直觉,适用于开关标志、计数器、状态机切换等场景;初学者应优先使用,避免因误用 relaxed/acquire/release 导致难以复现的并发 bug。

什么时候必须用 memory_order_seq_cst
绝大多数人写多线程 C++ 时,根本不需要动其他 memory order —— 直接用 memory_order_seq_cst 最安全、最符合直觉。它保证所有线程看到的原子操作顺序一致,就像加了全局锁一样“整齐”。
常见场景:std::atomic<bool></bool> 做开关标志、std::atomic<int></int> 计数器、状态机切换(如从 INIT → RUNNING → STOPPED)。
- 如果你没读过《C++ Concurrency in Action》第 5 章,或没亲手复现过重排 bug,就别碰
relaxed/acquire/release -
seq_cst在 x86 上通常不额外生成 fence 指令(性能损失小),但在 ARM/AArch64 上会插dmb ish,有可见开销 - 错误现象:用了
relaxed后,一个线程改了值,另一个线程死循环读不到更新(编译器 or CPU 重排导致缓存未同步)
acquire 和 release 配对才能传递同步语义
单用 acquire 或 release 没意义 —— 它们只在成对出现时,构成一个“synchronizes-with”关系,让非原子变量的读写能跨线程可见。
典型模式:生产者写数据 + 写 flag;消费者读 flag + 读数据。flag 必须用 release 写、acquire 读。
立即学习“C++免费学习笔记(深入)”;
- 不能把
acquire用在写操作上(编译器报错);release不能用在读操作上 - 如果 flag 是
std::atomic<bool></bool>,生产者:data = 42; flag.store(true, std::memory_order_release);;消费者:if (flag.load(std::memory_order_acquire)) { use(data); } - 漏掉任意一端的 memory order,
data的修改可能对消费者不可见(即使 flag 已为 true)
memory_order_relaxed 只适用于计数器、引用计数等无依赖场景
relaxed 唯一合法用途:你只关心原子性,完全不依赖它和其他内存访问的顺序 —— 比如 std::shared_ptr 内部的引用计数,或性能敏感的统计计数器。
错误用法:用它来实现锁、信号量、状态通知,或和非原子变量混用。
- 常见误用:
counter.fetch_add(1, std::memory_order_relaxed)后立刻检查counter.load()是否到阈值 —— 这个检查结果可能被重排到加之前 - ARM/PowerPC 上,
relaxedload/store 可能乱序执行,x86 虽然有强序模型,但编译器仍可能重排(比如把后面的 load 提前) - 调试时加
std::atomic_thread_fence(std::memory_order_seq_cst)往往能让 bug 消失,但这只是掩盖问题,不是修复
为什么 consume 被弃用、acq_rel 很少需要
memory_order_consume 理论上比 acquire 更轻量,但它依赖“数据依赖链”做同步,而编译器几乎无法可靠追踪这种依赖(尤其经过内联、优化后)。C++17 起已标记为 deprecated,实际项目中应避免使用。
acq_rel 只用于 read-modify-write 操作(如 fetch_or、compare_exchange_weak),且仅当你明确需要“读取时 acquire + 写入时 release”的语义 —— 大多数 RMW 场景其实用 seq_cst 更清晰。
- 比如自旋锁的
compare_exchange_weak:成功获取锁用acq_rel,失败重试用relaxed;但初学者直接全用seq_cst更不容易出错 - Clang/GCC 对
consume的实现基本等价于acquire,没带来收益,反而增加理解成本 - LLVM 文档里明确说:“Don’t use
consume. Ever.”
seq_cst 把逻辑跑通。











