std::memory_order_relaxed仅保证原子性,不约束指令重排,适用于计数器、状态标志等无需同步顺序的场景;禁用于同步点或依赖读写顺序的逻辑,且需避免伪共享和错误使用compare_exchange_weak。

原子操作本身不慢,慢的是你没控制好内存序、缓存行和访问模式。默认用 std::memory_order_seq_cst 或者让多个原子变量挤在同一个缓存行里,性能会掉得比锁还狠。
memory_order_relaxed 能用就用
绝大多数计数器、状态标志、统计量更新都不需要全局顺序一致——它们只关心“值变了”,不关心“谁先谁后”。std::memory_order_relaxed 去掉了编译器和 CPU 的重排屏障,指令更轻,执行更快。
- 适用场景:
counter.fetch_add(1, std::memory_order_relaxed)、flag.store(true, std::memory_order_relaxed) - 不能用的场景:作为同步点(比如线程启动信号)、依赖读写顺序的逻辑(如 double-checked locking)
- Clang 18 / GCC 14 会对
relaxed操作做跨函数优化,但前提是变量生命周期清晰、无别名干扰
避免伪共享(false sharing)
两个线程频繁修改不同变量,但如果它们落在同一缓存行(通常是 64 字节),就会反复触发总线无效化,导致性能暴跌。这不是原子操作的问题,是布局问题。
- 用
alignas(64)强制对齐,确保每个std::atomic独占一行:struct alignas(64) Counter { std::atomic<int> value{0}; };</int> - 数组中多个原子变量要间隔 64 字节,不要直接写
std::atomic<int> counters[4]</int>—— 它们大概率挨着放 - NUMA 架构下,还要注意变量分配在哪个 node;跨 node 访问原子变量延迟可能翻倍
慎用 compare_exchange_weak 的重试逻辑
compare_exchange_weak 在某些架构(如 ARM)上可能因内部冲突“假失败”,必须配合循环使用。但写错重试条件或忘记更新 expected,会导致无限循环或逻辑错误。
立即学习“C++免费学习笔记(深入)”;
- 典型错误写法:
while (!counter.compare_exchange_weak(expected, expected + 1)) {}——expected没更新,死循环 - 正确写法:
while (!counter.compare_exchange_weak(expected, expected + 1)) {}前必须确保expected是最新值,通常从load()获取或在循环内更新 - 高竞争下,
weak比strong更快,但仅当能容忍重试时才用;低竞争或逻辑简单时,fetch_add往往更直接
别把 atomic 当普通变量用
原子变量禁止拷贝、不能取地址(&var 会编译失败)、不能用于某些模板推导上下文。试图绕过这些限制,比如用 reinterpret_cast 或裸指针操作底层存储,等于放弃原子语义。
-
std::atomic不是int的子类,不能隐式转换;必须显式调用.load()或.store() - 结构体里嵌套
std::atomic成员没问题,但整个结构体不能直接atomic<MyStruct>—— 除非它满足 trivially copyable + 标准布局等严格条件 - 调试时打印原子变量值,必须用
counter.load(),而不是std::cout << counter(会报错)
最常被忽略的一点:原子操作的性能瓶颈往往不在指令本身,而在你让它和非原子数据共享缓存行、或在 hot path 上用了过强的内存序。优化前先用 perf record -e cache-misses 看看是不是伪共享在拖后腿,而不是一上来就换内存序。



















