c++的std::atomic的compare_exchange_weak和strong如何选择? (无锁编程)

14次阅读

compare_exchange_weak 可能虚假失败是因为底层LL/SC架构(如ARM)允许stxr即使值匹配也失败,这是硬件特性而非bug;weak放弃兜底以换性能,需配合do-while循环使用。

c++的std::atomic的compare_exchange_weak和strong如何选择? (无锁编程)

compare_exchange_weak 为什么有时会“虚假失败”?

compare_exchange_weak 在底层可能被编译器或硬件映射为一条非原子的“加载-比较-条件存储”序列(如 x86 的 cmpxchg 是强的,但 ARM 的 ldxr/stxr 对本身就允许 spurious failure)。这意味着即使当前值等于期望值,它也可能返回 false,不修改目标值,也不保证重试一定能成功——纯粹是硬件/指令集特性导致的“假失败”。

这不是 bug,而是设计取舍:weak 版本放弃对这类失败的兜底处理,换来更低的指令开销和更高的并发吞吐。

  • 常见错误现象:compare_exchange_weak 在循环中反复失败,但 load() 显示值始终没变
  • 必须配合 do-while 循环使用,不能单独依赖一次调用结果
  • 在 LL/SC(Load-Linked/Store-Conditional)架构(ARM、RISC-V)上更容易触发虚假失败;x86 上 weak 和 strong 行为几乎一致,但语义仍不同

什么时候必须用 compare_exchange_strong?

当你需要「一次调用,语义确定」——即:只要当前值等于期望值,就必须成功更新并返回 true。这在非循环逻辑、状态机跃迁、或仅尝试一次的场景中不可妥协。

  • 典型场景:实现一个一次性初始化标志(如 std::call_once 底层)、资源首次注册、或者作为更大原子操作中的关键判断分支
  • 如果写成 if (flag.compare_exchange_strong(expected, desired)) { ... },你依赖的是“成功即发生”,不能容忍假失败干扰控制流
  • 性能代价:strong 在某些平台(尤其是 ARM)需额外重试逻辑或内存屏障,延迟略高,高争用下吞吐可能下降 10–20%

无锁循环里 weak 反而是默认首选

绝大多数无锁数据结构、队列、计数器)的 CAS 循环,都应优先写 compare_exchange_weak。它的轻量特性在高并发下更友好,且循环本身已天然吸收了虚假失败。

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

Node* old_head = head.load(); Node* new_head; do {     new_head = old_head->next; } while (!head.compare_exchange_weak(old_head, new_head));
  • 这里用 weak 是合理的:循环体足够简单,失败后重新读 old_head 成本低
  • 若误用 strong,在 ARM 上可能多一次 ldxr + 条件重试,而 weak 失败后直接下一轮 loop,实际更快
  • 只有当循环体开销极大(比如涉及多次 cache miss 内存访问),才需权衡是否用 strong 减少重试次数——但这往往说明设计该优化了

别忽略 memory_order 参数的一致性影响

compare_exchange_weakcompare_exchange_strong 都接受两个 memory_order 参数(success/failure),但很多人只传 std::memory_order_seq_cst,忽略了 failure 路径其实可以更宽松。

  • failure 分支通常只是重试,不需要同步语义,用 std::memory_order_relaxed 即可
  • 常见错误写法:cas(..., std::memory_order_seq_cst, std::memory_order_seq_cst) —— 多余的全局顺序开销
  • 推荐模式:cas(..., std::memory_order_acq_rel, std::memory_order_acquire)(成功时带 acquire+release,失败时只需 acquire 保证重读有效)

weak/strong 的选择和 memory_order 是正交问题,但混在一起容易放大性能偏差。虚假失败本身不改变内存序语义,但 failure 的 order 设得太强,会拖慢整个循环。

text=ZqhQzanResources