std::atomic

4 阅读2分钟

原子操作

std::atomic 禁用了拷贝构造函数和拷贝赋值运算符(因为拷贝操作无法原子化),所以不能直接 std::atomic<int> d = a;

函数内容
atomic (T val)构造
operator=构造,等号后面是 T 类型, 不能是atomic
{}大括号直接初始化
is_lock_free()用于判断 std::atomic 的操作是 “真无锁(CPU 原子指令)” 还是 “假无锁(内置锁模拟)
store修改值
load获取值
operator T()获取值
exchange用 val 替换所含值,并返回该值在操作前的原始值。
compare_exchange_weak读取值并在比较成功时执行替换操作(替换成其他值)
compare_exchange_strong读取值并在比较成功时执行替换操作(替换成其他值)
fetch_add原值加上val, 返回操作前值
fetch_sub原值减val, 返回操作前值
fetch_and原值与val, 返回操作前值
fetch_or原值或val, 返回操作前值
fetch_xor原值异或val, 返回操作前值
operator++
operator--
operator+=等同于fetch_add
operator-=等同于fetch_sub
operator&=等同于fetch_and
operator|=等同于fetch_or
operator^=等同于fetch_xor

CAS 操作(Compare-And-Swap)

compare_exchange_weak/compare_exchange_strongstd::atomic 的核心,几乎所有无锁算法都基于 CAS 实现。
整个过程是不可中断的,完全由 CPU 硬件保证原子性,无需任何锁。
一句话概括:
我认为变量现在的值是 A,如果是,就把它改成 B;如果不是(说明被其他线程改了),就告诉我现在的值是多少,我不修改。

原理:
  • 比较原子变量的当前值与 “预期值(expected)”;
  • 如果相等:将原子变量更新为 “新值(desired)”,返回 true
  • 如果不相等:将 “预期值” 更新为原子变量的当前值,返回 false
  • 整个过程是原子的。
bool compare_exchange_strong(
    T& expected,                // 输入:预期值;输出:实际值(失败时)
    T desired,                  // 目标值(成功时要设置的值)
    std::memory_order success,  // 成功时的内存序
    std::memory_order failure   // 失败时的内存序
) noexcept;
bool compare_exchange_weak (T& expected, T val, memory_order success, memory_order failure) 

compare_exchange_strong和compare_exchange_weak区别在于伪失败
伪失败: 即使 当前值 == expectedweak 版本也可能返回 false(CPU 指令层面的偶然失败),但这种情况极少。
compare_exchange_strong性能低,但不会伪失败。
循环中用 weak 更高效 —— 即使偶尔伪失败,重试成本也远低于 strong 版本的严格检查。

#include <atomic>
#include <iostream>
int main() {
    std::atomic<int> num{10};
    int expected = 10;
    int desired = 20;
    // 循环重试,直到 CAS 成功
    while (!num.compare_exchange_weak(expected, desired)) {
        // 失败后,expected 已被更新为 num 的实际值,无需手动重置
        std::cout << "CAS 失败,当前 expected:" << expected << std::endl;
    }
    std::cout << "CAS 成功,num 当前值:" << num << std::endl;
    return 0;
}

自定义类型

自定义类型不建议使用std::atomic
建议使用`std::mutex` 加锁
C++20+:优先用 `std::atomic_ref` 简化自定义类型的原子操作。