std::atomic是首选,因其将读-改-写操作编译为单条cpu原子指令,无锁、无等待、高性能;而互斥锁开销大、易死锁、可能阻塞。

为什么 std::atomic 是首选,而不是互斥锁?
因为计数器操作(如自增)本质是读-改-写,普通 int 在多线程下会丢失更新;用 std::mutex 虽然能保证安全,但开销大、易死锁、还可能阻塞。而 std::atomic<int></int> 把这个操作编译成单条 CPU 原子指令(如 x86 的 inc 或 xadd),无锁、无等待、性能高。
适用场景:高频计数(如请求统计、引用计数)、低延迟要求、或嵌入式等资源受限环境。
注意:不是所有类型都支持原子操作——只有 trivially copyable 类型才能做 std::atomic,且必须确保对齐(std::atomic<int></int> 通常没问题,但 std::atomic<:string></:string> 不合法)。
fetch_add 和 operator++ 有什么区别?
两者都做原子加一,但返回值不同:fetch_add(1) 返回加之前的值(类似 i++),operator++() 返回加之后的值(类似 ++i)。选哪个取决于业务逻辑是否依赖返回值。
立即学习“C++免费学习笔记(深入)”;
常见错误:误以为 counter++ 总是返回新值——其实它调用的是后置递增,返回旧值;若想拿到新值,得用 ++counter 或显式写 counter.fetch_add(1) + 1。
示例:
std::atomic<int> counter{0};
int a = counter++; // a == 0,counter 变为 1
int b = ++counter; // b == 2,counter 变为 2
内存序(memory order)怎么选?
默认用 std::memory_order_seq_cst(顺序一致性),最安全也最慢;但在多数计数器场景中,你其实只需要“修改不被重排”+“其他线程能看到最新值”,这时可用更宽松的 std::memory_order_relaxed。
容易踩的坑:
- 混用不同内存序时,别假设读操作一定能立刻看到写——
relaxed不提供同步语义 - 如果计数器变化要触发其他动作(比如达到阈值就发通知),就不能只用
relaxed,得搭配acquire/release配对 - ARM/PowerPC 等弱序架构下,
seq_cst会插入额外屏障指令,影响性能
简单计数器推荐:
counter.fetch_add(1, std::memory_order_relaxed);
初始化和静态生命周期要注意什么?
std::atomic 是 POD 类型,静态变量(如全局或 static 局部)能零初始化,但不能用非常量表达式初始化(比如 std::atomic<int> x{get_init_value()};</int> 在全局作用域会报错)。
常见错误现象:undefined reference to `__atomic_fetch_add_4' —— 这是链接时缺了 libatomic(尤其在旧版 GCC 或交叉编译时),需加 -latomic。
建议写法:
- 全局/静态:直接
std::atomic<int> counter{};</int>(零初始化)或std::atomic<int> counter{42};</int>(常量初始化) - 类成员:在构造函数初始化列表里赋初值,不要在构造函数体内用
=赋值 - 避免用
new std::atomic<int>(42)</int>后再delete——没必要,也不安全
复杂点在于:原子变量不可拷贝、不可移动,所以不能放进 std::vector<:atomic>></:atomic>;真要存多个,得用 std::vector<:unique_ptr>>></:unique_ptr> 或封装成结构体。









