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

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_weak 和 compare_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 设得太强,会拖慢整个循环。










