compare_exchange_weak更适合计数,因其允许虚假失败且需配合循环重试,失败时自动更新expected为当前值,x86上性能与strong相近,ARM上更轻量;必须置于while/do-while循环中,不可单次调用。

compare_exchange_weak 为什么比 compare_exchange_strong 更适合计数
因为 compare_exchange_weak 允许虚假失败(spurious failure),在 x86 上通常编译为单条 cmpxchg 指令,无循环开销;而计数场景天然需要重试逻辑,虚假失败不影响正确性,反而避免了 compare_exchange_strong 在某些架构(如 ARM)上可能引入的隐式循环,实际性能更稳。
常见错误是把它当一次性操作用——比如没包在 while 循环里,导致更新丢失:
int expected = counter.load(); counter.compare_exchange_weak(expected, expected + 1); // ❌ 可能失败且不重试
标准原子计数循环怎么写才安全
必须用 do-while 或 while 循环包裹,每次失败后用新值重试。关键点在于:更新 expected 不能靠加 1,而要靠 compare_exchange_weak 失败后自动写回的当前值。
-
expected初始值从load()获取,但后续只依赖compare_exchange_weak的输出更新 - 不要在循环体内修改
expected后再手动赋值,否则会跳过并发修改 - 如果想读取旧值并返回,需在循环前保存一份副本
std::atomic<int> counter{0};
int old_val;
int expected = counter.load();
do {
old_val = expected;
expected = counter.load(); // 可选:也可直接用 old_val,但 load 更清晰
} while (!counter.compare_exchange_weak(expected, expected + 1));
compare_exchange_weak 的返回值和参数顺序容易踩哪些坑
函数签名是 bool compare_exchange_weak(T& expected, T desired),返回 true 表示成功(内存值 == expected,已设为 desired);false 表示失败,并把当前实际值写入 expected。很多人误以为 expected 是输入参数、不会被改写,结果循环条件写成 expected != desired,导致死循环或跳过更新。
立即学习“C++免费学习笔记(深入)”;
典型错误写法:
int expected = counter.load();
while (counter.compare_exchange_weak(expected, expected + 1) == false) {
expected++; // ❌ 错!expected 已被 compare_exchange_weak 覆盖为当前值,再 ++ 就错乱了
}
正确做法是让 expected 始终反映最新观测到的值,不手动干预:
int expected = counter.load();
while (!counter.compare_exchange_weak(expected, expected + 1)) {
// expected 已被更新为当前值,直接下一轮重试
}
用 int 还是用 uint32_t?溢出时行为是否可预测
std::atomic<int></int> 的 compare_exchange_weak 对溢出不做检查,行为由底层整型定义:有符号溢出是未定义行为(UB),所以计数器必须确保不会溢出,或改用无符号类型。
- 推荐用
std::atomic<uint32_t></uint32_t>或std::atomic<uint64_t></uint64_t>,溢出是模运算,确定性高 - 若必须用
int,需在业务层做阈值检查(比如expected ),但检查和 CAS 之间仍有竞态,严格场景建议配合 <code>fetch_add -
fetch_add更简洁安全,除非你需要“仅当等于某值时才加”这种条件语义
真正需要 compare_exchange_weak 的计数场景,往往是复合操作,比如“加 1 且小于阈值才成功”:
uint32_t expected = counter.load();
do {
if (expected >= MAX_COUNT) break;
} while (!counter.compare_exchange_weak(expected, expected + 1));
注意:compare_exchange_weak 的“弱”不是指线程安全弱,而是指允许虚假失败;它的原子性和内存序保障与其他 atomic 操作一致。最容易被忽略的是——它不保证失败时 expected 一定被更新(极少数平台可能不写),所以循环中必须重新 load() 或依赖其输出,不能假设失败后 expected 一定变。


















