结论:compare_exchange_weak 是循环中更新原子变量的首选,因其在x86/ARM上生成更紧凑指令、更低延迟;它通过暴露硬件伪失败并依赖循环兜底,避免了strong版本的额外分支开销。

直接说结论:在循环中更新原子变量时,compare_exchange_weak 是首选;它可能伪失败,但正是这个“不保证一次成功”的特性,换来了 x86 和 ARM 上更紧凑的汇编指令和更低的延迟。
为什么 compare_exchange_weak 在循环里比 strong 更合适
它不是“不够强”,而是设计上就为重试场景服务的。x86 的 CMPXCHG 指令本身不会伪失败,但 ARM 的 LDXR/STXR 对在中断、缓存失效或上下文切换时可能失败——compare_exchange_weak 把这种硬件不确定性暴露出来,让程序员用循环兜底;而 compare_exchange_strong 内部会多包一层循环来屏蔽伪失败,反而增加分支预测压力和指令开销。
常见错误现象:compare_exchange_strong 在 ARM 低负载下看似更“稳”,但一到高并发就比 weak 版本吞吐低 10%~15%,尤其在队列尾指针更新这类热点路径上。
- 使用场景:所有需要“读-改-重试”的无锁结构,比如栈 push/pop、队列 enqueue/dequeue、计数器 increment
- 性能影响:weak 版本在 clang/gcc 下常被编译成单条
stxr(ARM)或cmpxchg(x86),strong 版本则多出条件跳转和重试逻辑 - 兼容性注意:不要在非循环上下文中单独调用 weak,否则伪失败会导致逻辑跳过,比如 if 分支里只写一次 weak 调用,大概率漏更新
expected 参数为什么必须是引用,且会被函数修改
这是 CAS 循环能自维持的关键机制。compare_exchange_weak 失败时,不是简单返回 false,而是把原子变量当前真实值写回你传入的 expected 引用变量里。这样下一轮循环无需重新 load,直接拿更新后的值继续试。
示例代码片段:
std::atomic<int> counter{0};
int expected = counter.load(std::memory_order_relaxed);
int desired;
do {
desired = expected + 1;
} while (!counter.compare_exchange_weak(expected, desired,
std::memory_order_release, std::memory_order_relaxed));
</int>
这里 expected 是 int&,每次循环开头的 desired = expected + 1 用的就是上一轮失败后自动刷进来的新快照。如果传的是值拷贝,就永远卡在旧值上。
- 容易踩的坑:把
expected声明为const int&或直接传字面量,编译不过 - 内存序建议:failure 路径用
std::memory_order_relaxed就够了,因为失败时没写操作;success 路径按语义选,如栈 push 用std::memory_order_release,pop 用std::memory_order_acquire
compare_exchange_weak 的两个内存序参数怎么配
双参数版本(compare_exchange_weak(T& expected, T desired, memory_order success, memory_order failure))不是炫技,而是为避免过度同步。failure 内存序必须弱于或等于 success,且不能是 memory_order_release 或 memory_order_acq_rel——因为失败路径根本不执行写,设释放语义没意义。
典型搭配:
- 无锁栈 push:success =
std::memory_order_release,failure =std::memory_order_relaxed - 无锁队列 tail 更新:success =
std::memory_order_acquire(需看到之前节点构造完成),failure =std::memory_order_relaxed - 错误配置后果:failure 设成
std::memory_order_seq_cst会导致每次失败都触发全核内存栅栏,性能断崖式下跌
真正难的不是写对第一个 CAS 循环,而是确保所有共享状态(比如环形队列里的 head、tail、tailCommit)的更新顺序和可见性相互匹配;一个变量用 relaxed,另一个却漏掉 acquire,bug 可能几个月都不复现,但一上生产就丢数据。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











