直接用 int 自增会出错,因为多线程下 i++ 被拆为读-加-写三步,导致竞态;std::atomic 通过硬件级原子指令(如 lock xadd)保证操作不可分割,支持初始化、fetch_add、load/store/exchange 等操作,并需注意类型限制与内存序选择。

为什么直接用 int 自增会出错?
多线程环境下,i++ 看似一条语句,实际拆成三步:读内存 → 加1 → 写回。两个线程可能同时读到旧值,各自加1后再写回,结果只加了1次而非2次。这不是编译器优化问题,是硬件级的竞态本质。
用 std::mutex 能解决,但锁有开销,且可能阻塞、死锁。原子操作让 CPU 直接提供“读-改-写”不可分割的指令(如 x86 的 lock xadd),绕过锁也能保证正确性。
std::atomic<int></int> 怎么初始化和自增?
最简用法就是声明 + 调用成员函数,不需要额外头文件(<atomic></atomic> 已被包含在常用标准头中):
std::atomic<int> counter{0};
counter.fetch_add(1, std::memory_order_relaxed); // 返回旧值
// 或
counter++; // 等价于 fetch_add(1, memory_order_seq_cst)
注意:fetch_add 默认使用 std::memory_order_seq_cst(顺序一致性),安全但稍慢;若只关心计数本身、不依赖其他变量的读写顺序,可用 std::memory_order_relaxed 提升性能。
立即学习“C++免费学习笔记(深入)”;
-
load()和store()用于单纯读写,不带修改 -
exchange()原子替换,返回旧值 - 所有操作都隐式禁止编译器重排,但内存序决定 CPU 是否重排——这点常被忽略
哪些类型能用 std::atomic?
不是所有类型都能原子化。内置整型(int、long long)、指针、以及满足 is_trivially_copyable 且尺寸不超过平台最大原子宽度(通常 ≤16 字节)的 POD 类型才支持特化。
例如:std::atomic<:string></:string> 编译失败;std::atomic<:shared_ptr>></:shared_ptr> 可用,因为指针本身是整数宽度;自定义结构体若含虚函数、非 trivial 析构或对齐超标,也不行。
判断方法:运行时检查 counter.is_lock_free()。返回 true 表示底层用 CPU 原子指令实现(高效);false 表示退化为内部互斥锁(仍线程安全,但失去无锁意义)。
无锁 ≠ 没有陷阱
原子变量解决的是单个变量的并发修改问题,但无法自动保护多个变量之间的逻辑关系。比如“计数器达到阈值就触发清理”,这个“判断+触发”必须整体原子,否则仍需锁或更复杂的无锁结构(如 RCUs)。
另一个坑是误用 memory_order_relaxed:它只保证该原子操作自身不被重排,不约束周边普通读写。若计数器变化需同步其他数据(如缓存状态),必须升级内存序,否则可能看到过期值。
最后提醒:无锁编程调试困难,std::atomic 是工具,不是银弹。先确认瓶颈真在锁上,再考虑替换。多数场景下,std::mutex 更稳妥、更易维护。


















