std::atomic::load必须指定memory_order,因默认std::memory_order_seq_cst开销最大;实际中依场景选acquire(同步)或relaxed(独立读取)可提升性能。

std::atomic::load 为什么必须指定 memory_order?
不指定内存顺序时,std::atomic::load 默认用 std::memory_order_seq_cst,性能开销最大。它强制全局顺序一致,但多数场景不需要这么强的保证——比如只读共享计数器、状态标志位,用更弱的顺序就能避免不必要的内存屏障。
- 默认行为等价于
load(std::memory_order_seq_cst),x86 上会插入mfence或隐含序列化语义 - 若变量仅用于同步(如通知另一个线程“数据已就绪”),
std::memory_order_acquire就够了 - 若只是原子读取一个独立计数器(无依赖其他内存操作),
std::memory_order_relaxed最快,但编译器和 CPU 都可能重排它前后的普通读写
std::memory_order_acquire 和 std::memory_order_relaxed 的实际区别
关键不在“能不能读到最新值”,而在于它对周围普通内存访问的约束力。假设有一个 flag 和一个 data:
std::atomic<bool> ready{false};
int data = 0;
// 线程 A
data = 42;
ready.store(true, std::memory_order_release); // 保证 data 写入在 store 前完成
// 线程 B
if (ready.load(std::memory_order_acquire)) { // 保证后续读 data 不会早于这个 load
std::cout << data << "\n"; // 此处一定能读到 42
}
如果把 load 改成 std::memory_order_relaxed,data 的读取就可能被重排到 load 之前,从而读到 0。
-
std::memory_order_acquire:禁止该 load 后的普通读写被重排到它前面 -
std::memory_order_relaxed:只保证原子性,不提供任何同步或顺序约束 -
std::memory_order_consume已基本弃用,不要用
哪些场景适合用 std::memory_order_relaxed
适用于纯统计、计数、或仅靠外部同步机制(如互斥锁)保护的数据。例如:
立即学习“C++免费学习笔记(深入)”;
std::atomic<int> counter{0};
// 多线程并发自增,但最终结果只用于日志或监控,不要求实时精确
counter.fetch_add(1, std::memory_order_relaxed);
- 高频计数器(如请求量、错误次数)——只要不崩溃,偶尔少加一次也无所谓
- 引用计数中的递减(配合
std::memory_order_acq_rel读-改-写) - 与
std::memory_order_release配对的 load 场景不存在,relaxedload 不能同步其他线程的写
容易踩的坑:混用 memory_order 导致未定义行为
同一个原子变量上,load 和 store 的内存序不匹配,可能破坏同步逻辑。最典型的是:
- 用
std::memory_order_releasestore,却用std::memory_order_relaxedload —— 无法建立 happens-before 关系,配套的非原子变量读写可能看到陈旧值 - 在 x86 上看似正常(因为强序),但换到 ARM/AArch64 就出问题:那里
relaxedload 真的不带 acquire 语义 -
std::memory_order_seq_cst虽安全,但和acquire/release混用时,可能意外拖慢整个程序——它要求所有 seq_cst 操作全局排序
真正难的不是记住每个枚举值的定义,而是判断“这里到底需要同步什么”。往往得画出数据依赖图,再决定用哪一档顺序。没想清楚就硬套 seq_cst,等于放弃原子操作的大部分价值。


















