std::atomic 解决多线程下变量读-改-写竞态问题,通过硬件/编译器级原子操作保证 load/store、++、CAS 等操作不可分割;支持平凡可复制的小型类型,需显式声明,不替代 mutex 的多步逻辑保护。
当多个线程同时读写同一个变量(比如 int counter = 0;),++counter 不是原子的:它实际包含「读取→加1→写回」三步。两个线程可能同时读到 0,各自加 1 后都写回 1,结果丢失一次递增。这就是竞态条件。std::atomic 把这类操作打包成不可打断的单步指令,让读-改-写变成一个整体,从硬件或编译器层面阻止交错。
常见整型、指针类型支持直接替换,但必须显式声明为 std::atomic,不能对普通变量临时套用原子函数。以下操作默认是原子的(无需额外锁):
load() 和 store(value) —— 带内存序的读/写operator++()、operator+=() —— 复合赋值(如 counter++)compare_exchange_weak(expected, desired) —— CAS(Compare-And-Swap),实现无锁数据结构的核心注意: std::atomic、std::atomic、std::atomic 是标准保证可锁自由(lock-free)的;而 st 不合法 —— 类型必须是平凡可复制(trivially copyable)且大小适中(通常 ≤ 指针宽度)。
默认使用 std::memory_order_seq_cst(顺序一致性),最安全但也最慢 —— 编译器和 CPU 会插入大量屏障防止重排。实际中可根据场景降级:
std::memory_order_relaxed(如计数器)std::memory_order_acquire(读) + std::memory_order_release(写)配对relaxed,成功路径才考虑更强序错误示例:
std::atomic这里必须用flag{0}; // 线程 A: flag.store(1, std::memory_order_relaxed); // 这个 store 可能被重排到后面语句之后 data_ready = true; // 线程 B: if (flag.load(std::memory_order_relaxed) == 1) { use(data_ready); // data_ready 可能还是 false! }
release/acquire 配对才能保证 data_ready 的写入对 B 可见。
它只保单个变量的读写原子性,无法保护多步逻辑。比如「检查值是否为 0,是则设为 1」需要 CAS 循环,而「先删节点再更新头指针再释放内存」这种多对象协作,仍需 std::mutex 或更高级的无锁设计。
另外,std::atomic 对象本身不可拷贝(只有移动或赋值),也不能作为容器元素直接存储(需用 std::vector<:atomic>*> 或包装成 struct);调试时打印其值要用 .load(),直接 std::cout 会编译失败。