c++的std::memory_order是什么 原子操作的内存序详解【并发进阶】

10次阅读

std::memory_order 是一组枚举值,用于约束原子操作周围内存访问的重排行为及跨线程可见性,不改变原子性本身;它解决编译器 /CPU 重排导致的多线程同步失效问题,含 relaxed、consume(弃用)、acquire、release、acq_rel、seq_cst 六种,其中 acquire-release 配对构成核心同步模型。

c++ 的 std::memory_order 是什么 原子操作的内存序详解【并发进阶】

std::memory_order 是什么

它是一组枚举值,用来告诉编译器和 CPU:在执行原子操作时,周围的普通读写操作 ** 可以怎么重排 **、** 对其他线程可见的顺序如何保证 **。它不改变原子操作本身的原子性,而是控制该操作与其他内存访问之间的 ** 同步关系和可见性边界 **。

为什么 需要内存序

现代 CPU 和编译器为了性能,会做两类重排:

  • 编译器重排:在不改变单线程语义的前提下,调整指令顺序(比如把读操作提前)
  • CPU 重排:乱序执行、写缓冲区延迟刷新、缓存不一致等,导致多线程下看到的执行顺序和代码顺序不一致

如果没有显式约束,两个线程可能永远看不到彼此的修改——看似正确的原子变量,实际无法完成同步。内存序就是为了解决这个问题而设的“围栏”或“承诺”。

六种 memory_order 及典型用途

注意:所有 memory_order 都只对当前这条原子操作生效,影响的是它与前后非原子 / 原子访问的相对顺序。

立即学习C++ 免费学习笔记(深入)”;

  • memory_order_relaxed:最弱。仅保证该原子操作自身是原子的,不施加任何顺序约束。适用于计数器、句柄生成等无需同步的场景。
  • memory_order_consume:已基本被弃用。理论上用于数据依赖关系(如指针解引用),但因实现复杂且易出错,实践中建议用 acquire 替代。
  • memory_order_acquire:用于读操作(load)。保证该 load 之后的所有读写(包括非原子)不能被重排到它前面。常作为“获取锁”或“读取共享数据前的同步点”。
  • memory_order_release:用于写操作(store)。保证该 store 之前的所有读写(包括非原子)不能被重排到它后面。常作为“释放锁”或“写完共享数据后的同步点”。
  • memory_order_acq_rel:用于读 - 改 - 写操作(如 fetch_add、compare_exchange)。兼具 acquire 和 release 的语义,即前后都不可重排。
  • memory_order_seq_cst:默认值,最强。所有带此序的原子操作构成一个全局单一执行顺序,就像所有线程按某种统一时间线串行执行一样。安全但可能有性能代价。

acquire-release 配对是核心模式

这是最常用也最关键的同步模型。它不要求全局顺序,只要求两件事:

  • 线程 A 执行了 flag.store(true, memory_order_release),且之前写了数据 data = 42
  • 线程 B 执行了 if (flag.load(memory_order_acquire)) {use(data); }
  • 那么一旦 B 看到 flag == true,就一定能安全读到 data == 42 —— 编译器和 CPU 都不会把 data = 42 重排到 store(flag) 后面,也不会把 use(data) 重排到 load(flag) 前面

这种配对构成了“synchronizes-with”关系,是 C++ 内存模型中定义正确同步的基础。

别盲目用 seq_cst,但也别过早优化

初学时用 memory_order_seq_cst 没问题,逻辑清晰、不易出错;等你明确知道瓶颈在哪、且确认 relax/acquire/release 能满足语义时,再谨慎替换。比如 无锁 队列、RCU、信号量内部等场景才真正需要细粒度控制。多数业务代码里,用好 mutex 或 atomic + seq_cst 就足够安全。

以上就是

星耀云
版权声明:本站原创文章,由 星耀云 2025-12-19发表,共计1559字。
转载说明:转载本网站任何内容,请按照转载方式正确书写本站原文地址。本站提供的一切软件、教程和内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。
text=ZqhQzanResources