memory_order 是对编译器和CPU重排序行为的显式约束,非性能开关;选错可能导致陈旧值、乱序或死锁。默认应使用 memory_order_seq_cst,仅在明确需求且验证后才用更弱序。

memory_order 是什么,为什么不能乱选
它不是“性能开关”,而是对编译器和 CPU 重排行为的显式约束。选错不会立刻崩溃,但可能在多核、优化开启(-O2)、或换 CPU 架构后突然出现读到陈旧值、指令乱序执行、甚至死锁——而且极难复现。
关键判断:默认用 memory_order_seq_cst,除非你明确知道为什么需要更弱的顺序,且已验证过所有线程路径。
哪些场景必须用 memory_order_seq_cst
绝大多数日常同步逻辑都属于这一类:计数器增减、标志位设置+后续操作、生产者-消费者中的简单信号量。它的语义最接近“直觉上的原子操作”——所有线程看到的修改顺序一致,且与程序顺序一致。
- 多个
atomic<int></int>变量之间有逻辑依赖(比如先写ready再写data,消费者要按此顺序读) - 与非原子变量混用,且依赖其可见性(如写完
atomic_flag后更新全局结构体) - 调试阶段、原型代码、或不确定并发模型时
示例:counter.fetch_add(1, std::memory_order_seq_cst) —— 安全、可预测、开销在现代 x86 上几乎无感。
立即学习“C++免费学习笔记(深入)”;
memory_order_acquire/release 什么时候能用
这是最常被误用也最有价值的一组。它不保证全局顺序,只建立“同步关系”:一个 release 写 + 对应的 acquire 读,构成一个同步点,确保该点之前的所有内存操作对另一个线程可见。
典型适用场景:
- 自旋锁实现:
lock()用memory_order_acquire读标志,unlock()用memory_order_release写标志 - 单生产者单消费者无锁队列:生产者
storehead 用release,消费者loadhead 用acquire - 初始化完成标志:生产者写完数据后用
store(true, memory_order_release),消费者用load(memory_order_acquire)检查
注意:acquire/release 必须成对出现在不同线程的同一变量上;单独用 relaxed 或混搭 seq_cst 会破坏同步语义。
memory_order_relaxed 和 memory_order_consume 的坑
memory_order_relaxed 只保证原子性,不施加任何顺序约束。适合计数器(如引用计数)、状态标记(只要求“最终一致”,不要求其他线程何时看到),但极易误用。
常见错误:
- 用
relaxed实现“先设 flag 再写 data”,消费者用relaxed读 flag 后直接读 data —— data 可能未写入缓存,或被重排到 flag 之后 - 把
relaxed当作“性能更好所以优先选”,却没意识到它让推理线程交互变得几乎不可能
memory_order_consume 理论上比 acquire 更轻量,但实际支持极差:GCC/Clang 当前将其降级为 acquire,且容易因编译器优化引入意外重排。标准中已标记为“deprecated”,别用。
复杂点在于:即使你选对了 order,不同 CPU 架构(x86 vs ARM/AArch64)对相同 order 的硬件实现强度不同。x86 天然强顺序,ARM 则更依赖显式 barrier。测试必须在目标平台上做,不能只信本地跑通。


















