compare_exchange_weak必须放在循环中使用,因为它可能伪失败,即使值匹配也返回false;标准写法是do-while循环,每次失败后expected被自动更新,再重新计算desired以确保正确性。

compare_exchange_weak 为什么必须放在循环里用
因为 compare_exchange_weak 允许“伪失败”(spurious failure):即使当前值等于预期值,它也可能返回 false,不修改目标值。这不是 bug,是底层硬件(比如某些 ARM 架构)或编译器优化导致的合法行为。所以单次调用无法保证更新成功,必须重试。
标准循环写法长什么样
典型模式是“读-改-比-换”四步闭环,用 do-while 或 while 循环包裹:
std::atomic<int> counter{0};
int expected = counter.load();
int desired;
do {
desired = expected + 1;
} while (!counter.compare_exchange_weak(expected, desired));
</int>
关键点:
-
expected必须是变量(不能是字面量或临时量),因为失败时它会被compare_exchange_weak自动更新为当前实际值 - 循环体里要重新计算
desired,否则下次仍用旧的expected值,可能无限失败 - 不要在循环外初始化
expected后就不再读取——它必须反映每次重试前的最新状态
compare_exchange_weak 和 compare_exchange_strong 的选择
多数场景优先用 compare_exchange_weak:它在 x86 上通常编译为单条 cmpxchg 指令,无额外开销;而 compare_exchange_strong 在某些平台需加循环兜底,反而更重。
只在以下情况考虑 strong 版本:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 逻辑不允许任何重试(比如循环体里有不可重入操作)
- 你明确知道目标平台对
weak的伪失败率高(如旧版 ARM GCC) - 循环体极轻(比如只是自增),且你愿意为少一次分支预测失败换可读性
容易踩的坑:修改 expected 后没重读、忘记更新 desired
常见错误写法:
int expected = counter.load();
while (!counter.compare_exchange_weak(expected, expected + 1)) {
// 错!expected 还是旧值,下轮直接拿它+1,逻辑错乱
}
正确做法是让 expected 在失败后自动被更新,再基于它算新值:
int expected = counter.load();
int desired;
do {
desired = expected + 1;
} while (!counter.compare_exchange_weak(expected, desired));
// 失败时 expected 已被设为当前值,下轮直接用
注意:如果 desired 计算复杂(比如涉及多个原子变量协同),循环内务必重新获取所有依赖值,否则可能违反业务约束。
伪失败不是异常,是设计特性;循环不是补救措施,而是使用前提。漏掉重读 expected 或复用旧 desired,会让 CAS 退化成随机行为。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










