compare_exchange_weak必须置于do-while/while循环中且expected为可修改非const引用,否则因伪失败和expected未更新导致死循环或逻辑错误;它在ARM等平台允许虚假失败,并覆写expected为当前值。

必须写在 do-while 或 while 循环里,且 expected 必须是可修改的非 const 引用——漏掉任一条件,大概率死循环或逻辑错误。
为什么 compare_exchange_weak 总是返回 false?
常见现象:刚初始化一个 std::atomic<int></int>,设 expected = 0,调一次 compare_exchange_weak(expected, 1) 就返回 false,但值明明没被改过。
- 根本原因是它允许伪失败(spurious failure),尤其在 ARM/AArch64 平台,哪怕
expected和当前值相等,也可能返回false - 更关键的是:它会**覆写
expected为当前实际值**。如果传入的是字面量、临时变量或const int&,编译可能报错;侥幸通过后,expected无法更新,下次重试仍用旧值,导致无限失败 -
expected必须是普通变量(如int expected = atomic_var.load();),不能是const int exp = ...;或compare_exchange_weak(0, 1)
怎么写才不会卡死或漏竞争?
单次 if 尝试是错的——从读取到 CAS 这段时间,其他线程可能已修改目标值,导致“检查通过但写入覆盖”。
- 正确模式是让
expected在循环中始终反映最新快照:int expected = atomic_var.load(); do { int desired = expected + 1; // 注意:这里不能依赖 expected 做复杂计算(如 malloc、IO) } while (!atomic_var.compare_exchange_weak(expected, desired)); - 循环体必须轻量:避免锁、内存分配、系统调用;否则自旋成本过高
- 极端高争用下,
weak可能反复伪失败,可加std::this_thread::yield()退避,或前 N 次用weak,失败后切strong
compare_exchange_weak 和 compare_exchange_strong 怎么选?
不是“强就一定好”,而是看语义是否允许重试、失败代价是否可接受。
立即学习“C++免费学习笔记(深入)”;
- 优先用
weak:所有循环重试场景(无锁栈 push/pop、计数器递增、状态机跃迁)——它在 x86 上性能几乎和strong一致,在 ARM 上指令更轻(如LDAXR/STLXR),伪失败代价低 - 考虑用
strong:单次尝试逻辑(如初始化标志位)、失败需触发昂贵回退(日志+告警)、或伪失败会破坏业务语义(如金融事务原子提交) - 两者签名完全一致,但
failure内存序必须 ≤success;若用双参数版,failure不能是memory_order_release或memory_order_acq_rel
指针 CAS 的 ABA 问题真躲不开?
用 std::atomic<node></node> 实现无锁链表时,典型问题是:节点 A 被弹出、析构、新节点又恰好分配到同一地址,compare_exchange_weak 误判“还是原来的 A”,导致数据损坏或崩溃。
- 这不是函数 bug,而是裸指针 CAS 的固有缺陷
- 标准解法是带版本号(如
std::atomic<:pair size_t>></:pair>)或用std::shared_ptr(但注意:std::atomic<:shared_ptr>></:shared_ptr>不是标准特化,需用std::atomic<:shared_ptr>*></:shared_ptr>或第三方库) - 别指望靠
memory_order解决 ABA——它只管顺序,不防地址复用



















