std::memory_order 用于约束原子操作的编译器重排和cpu乱序,仅在多线程共享原子变量时生效;它通过acquire/release配对建立同步关系,seq_cst因全局顺序开销大而不作默认;memory order不保证非原子数据结构内部一致性。

std::memory_order 是用来约束原子操作的编译器重排和 CPU 乱序执行的
它不改变单线程行为,只在多线程读写共享原子变量时起作用。本质是给编译器和 CPU “划边界”:哪些指令不能跨过这个原子操作移动。
常见错误是以为 std::memory_order_relaxed 只是“性能更好”,结果发现线程间看不到最新值——其实它连最基本的同步语义都不提供,纯粹是原子读/写,不带任何顺序保证。
-
std::memory_order_acquire保证:该原子读之后的所有内存访问(读或写),不会被重排到该读之前 -
std::memory_order_release保证:该原子写之前的所有内存访问,不会被重排到该写之后 - 只有 acquire + release 配对使用时,才能建立“synchronizes-with”关系,从而让一个线程的写对另一个线程的读可见
acquire/release 不是锁,但能实现锁的部分效果
比如用 std::atomic<bool></bool> 实现自旋锁:写入 true 用 memory_order_release,读取 true 用 memory_order_acquire。这样临界区前的写、后的读,就不会“漏出”到锁外。
容易踩的坑是混用:store 用 relaxed,load 用 acquire —— 这无法同步;或者反过来,load 用 relaxed 却指望看到 release 写过的非原子变量值。
立即学习“C++免费学习笔记(深入)”;
- acquire 操作本身不阻止其他线程写,只是约束本线程指令重排
- release 操作本身不阻止其他线程读,同样只约束本线程
- 必须成对出现在不同线程、同一原子变量上,且一为 release、一为 acquire,才构成同步点
为什么 std::memory_order_seq_cst 不是默认选项
因为它是唯一要求全局顺序一致的模型,在 x86 上虽然硬件天然支持,但在 ARM/AArch64 上会插入额外的 dmb 指令,开销明显。而 acquire/release 在多数架构上可编译为无屏障的普通指令(如 ARM 的 ldar/stlr)。
典型误用是把所有原子操作都设成 seq_cst,以为“更安全”。其实只要逻辑上只需要 acquire-release 同步,强行用 seq_cst 就浪费了性能,还可能掩盖真正的数据竞争问题。
-
seq_cstload 等价于acquire+ 全局顺序约束 -
seq_cststore 等价于release+ 全局顺序约束 - 两个
seq_cst操作之间隐含一个全局单调递增的顺序,但代价是跨核同步成本高
调试时最常忽略的其实是 non-atomic 变量的读写顺序
acquire/release 只保护它“看到”的那些内存访问。如果你在 release 前改了一个 int data,又在 acquire 后读它,那没问题;但如果你在 release 前改的是 std::vector<int> v</int>,而 acquire 后只检查 v.size() 却没加锁,就可能读到部分构造的对象——因为 v 本身不是原子的,其内部指针更新不在 acquire/release 保护范围内。
换句话说:memory order 不解决数据结构的内部一致性,只解决“某个原子变量的读写”与其他内存操作之间的可见性边界。
- 非原子变量必须通过原子操作“发布”和“获取”,且发布/获取之间不能有未定义行为(如释放后访问)
- 哪怕用了
release,如果发布的指针指向未完全初始化的对象,acquire 后照样 UB - 工具如 ThreadSanitizer 能捕获这类问题,但它依赖你正确标注原子操作的 memory order










