c++中如何使用std::atomic_compare_exchange_c++原子操作CAS用法【详解】

10次阅读

std::atomic_compare_exchange_weak/strong 是标准定义的底层自由函数,但日常应使用 std::atomic::compare_exchange_weak/strong 成员函数;不存在 std::atomic_compare_exchange_cxx。

c++中如何使用std::atomic_compare_exchange_c++原子操作CAS用法【详解】

直接说结论:std::atomic_compare_exchange_weakstd::atomic_compare_exchange_strongc++ 原子 CAS 的实际入口函数,std::atomic::compare_exchange_weak/strong 成员函数才是日常该用的写法;裸调用自由函数版本几乎没理由 —— 它不自动推导内存序,且容易误传参数类型。

为什么不能直接用 std::atomic_compare_exchange_cxx 这种自由函数?

标准库中根本不存在叫 std::atomic_compare_exchange_cxx 的函数。你搜到的可能是拼写错误、过时资料,或把底层编译器内建函数(如 __atomic_compare_exchange_n)误当成了标准接口。C++ 标准只定义了两个自由函数:

  • std::atomic_compare_exchange_weak
  • std::atomic_compare_exchange_strong

但它们是为泛型算法设计的底层适配器,要求显式传入 std::atomic*、期望值指针、期望值本身、期望更新值、以及两套内存序 —— 容易出错,且和 std::atomic 对象的常规使用习惯脱节。

compare_exchange_weakcompare_exchange_strong 怎么选?

两者语义一致:比较当前值是否等于 expected,是则替换为 desired 并返回 true;否则将当前值写回 expected 并返回 false。关键差异在「失败行为」:

立即学习C++免费学习笔记(深入)”;

  • weak 版本允许「伪失败」(spurious failure):即使值匹配,也可能返回 false。常见于 LL/SC 架构(ARM、RISC-V),代价低、性能好
  • strong 版本保证:只要值匹配,就一定成功。x86 上二者汇编等价;但 ARM 上 strong 可能重试多次,开销略高

实践建议:

  • 循环 CAS 场景(如无锁 push/pop)—— 用 weak,配合 do-while 循环即可
  • 单次尝试、失败即放弃的逻辑(如初始化标志位)—— 用 strong,避免无谓重试

典型用法与易错点

必须注意:第二个参数是引用,且函数会修改它。这是最常踩的坑 —— 忘记取地址或传值导致编译失败或逻辑错误。

std::atomic counter{0}; int expected = 0; int desired = 1;  // ✅ 正确:expected 是引用,会被更新 if (counter.compare_exchange_strong(expected, desired)) {     // 成功:counter 从 0 → 1 } else {     // 失败:expected 已被设为 counter 当前值(比如 1)     // 此时 expected == 1,可继续用它重试 }  // ❌ 错误:传值,expected 不会被更新,下次 compare_exchange 还拿旧值比 // int expected = 0; // counter.compare_exchange_strong(expected, desired); // 编译不过(类型不匹配)  // ❌ 错误:取地址但没声明为引用,导致修改的是临时量 // counter.compare_exchange_strong(&expected, desired); // 编译不过(参数类型不对)

其他要点:

  • 默认内存序是 std::memory_order_seq_cst,安全但有开销;高频场景可指定更弱的序,如 compare_exchange_weak(expected, desired, std::memory_order_acquire, std::memory_order_relaxed)
  • std::atomic指针类型expected 类型必须严格匹配,不能隐式转换(例如不能用 nullptr 初始化 int* 引用)
  • 自定义类型 T 要求是 trivially copyable,且 sizeof(T) 通常需 ≤ 指针大小(否则可能降级为锁实现)

CAS 看似简单,但 expected 的生命周期、内存序搭配、weak/strong 的循环结构,三者稍有错位就会导致死循环、ABA 问题或数据竞争 —— 尤其在多核弱一致性架构上,别只盯着函数名,得盯住变量怎么传、谁改了它、改完之后你还信不信它。

text=ZqhQzanResources