compare_exchange_weak必须置于循环中,因弱内存模型平台存在伪失败,且其expected参数需为可修改的非const左值引用以自动更新;优先用于重试场景,而compare_exchange_strong适用于单次尝试或敏感业务。

compare_exchange_weak 必须写在循环里,否则大概率失败
它不是“试一次看行不行”的函数,而是设计为配合重试逻辑使用的。ARM、PowerPC 等弱内存模型平台允许伪失败(spurious failure),即当前值明明等于 expected,仍可能返回 false。x86 上虽少见,但标准不保证不会发生——所以单次调用基本等于放弃原子性保障。
常见错误现象:expected = 0,atomic_var.compare_exchange_weak(expected, 1) 返回 false,但你刚初始化过,没人改过它。
- 根本原因:函数内部会把
expected覆写为原子变量当前值;若没进循环,下次还拿旧值重试,永远卡住 - 正确姿势是用
do-while或while包裹,让expected在每次失败后自动更新 -
expected必须是非 const 的左值引用,不能是字面量、临时对象或const int&
expected 参数必须可修改,否则编译失败或逻辑崩溃
compare_exchange_weak 的第一个参数是 T& expected,签名决定了它要写回当前值。传入不可修改的变量,轻则编译报错,重则侥幸通过却导致死循环——因为失败时无法更新 expected,下一轮还是拿旧值比。
典型危险写法:
-
const int exp = atomic_var.load(); atomic_var.compare_exchange_weak(exp, 1);→ 引用绑定到 const,函数无法修改它 -
atomic_var.compare_exchange_weak(0, 1);→ 字面量不能取地址,编译不通过 -
int expected = atomic_var.load(); /* 忘了在循环里更新 */ atomic_var.compare_exchange_weak(expected, 1);→ 只调一次,且expected不再变化
✅ 正确写法:int expected = atomic_var.load(); while (!atomic_var.compare_exchange_weak(expected, desired)) { } ——失败时 expected 已被更新为最新值,下轮直接用
compare_exchange_weak vs compare_exchange_strong:别无脑选 strong
二者不是“强弱”意义上的优劣,而是语义与性能的权衡。weak 允许伪失败,但生成的汇编更轻量(ARM 上用 LDAXR/STLXR 而非 LDXR/STXR);strong 保证仅当值真实不等才失败,但每次失败代价更高。
- 优先用
compare_exchange_weak:所有循环重试场景,如无锁栈push/pop、环形队列头尾推进、计数器递增 - 考虑用
compare_exchange_strong:单次尝试逻辑(如初始化标志位)、失败需触发昂贵回退(日志+告警)、或伪失败会破坏业务语义(如金融事务提交) - 混合策略可行:前 N 次用 weak,失败后切 strong,避免极端争用下伪失败堆积
指针 CAS 的 ABA 问题不是 compare_exchange_weak 的锅
用 std::atomic<node></node> 实现无锁链表时,如果节点 A 被弹出、析构,新节点又恰好分配到同一地址,CAS 会误判“还是原来的 A”,导致链表断裂或访问野指针。这不是 compare_exchange_weak 的 bug,而是裸指针 CAS 的固有缺陷。
解决方向不在换函数,而在换数据结构设计:
- 用带版本号的指针(如
std::atomic<uint64_t></uint64_t>存ptr + version) - 用 hazard pointer 或 epoch-based reclamation 配合内存回收机制
- 改用
std::atomic<:shared_ptr>></:shared_ptr>延迟析构(但注意引用计数本身需原子)
真正容易被忽略的是:ABA 不是并发编程的“异常情况”,而是在真实长周期运行服务中大概率出现的问题——尤其在节点生命周期短、分配密集的场景下,不处理就等着 core dump 吧。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











