原子指令通过硬件保证“读—改—写”操作不可中断,解决多线程数据竞争问题,适用于计数器、标志位、引用计数及无锁结构,需注意伪共享、内存序和类型限制。

原子指令在多线程并发中解决的是最底层的“读—改—写”断裂问题。它不靠锁排队,而是靠硬件保证单条操作不可中断——比如 counter.fetch_add(1) 在 x86 上可能编译为带 LOCK 前缀的 add 指令,在 ARM 上则触发独占存储(strex/ldrex)机制。用对了,性能高、无死锁;用错了,照样出错。
什么时候必须用原子指令
当多个线程同时访问同一内存位置,且至少有一个是写操作时,就构成数据竞争风险。典型场景包括:
- 全局计数器(如请求统计、连接数、错误次数)
- 状态标志位(如
std::atomic<bool> ready{false}</bool>) - 资源引用计数(如智能指针内部的
use_count_) - 无锁数据结构中的头尾指针更新(需配合
compare_exchange_weak)
常用原子操作与对应语义
不要只记函数名,要理解它在并发逻辑中承担的角色:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
load():安全读取最新值。若仅需观察状态(如轮询
done.load()),默认memory_order_seq_cst足够;高频读可降为memory_order_acquire或relaxed(前提是不依赖其他变量) -
store(value):安全发布值。例如设置完成标志:
done.store(true, std::memory_order_release),确保之前所有计算已对其他线程可见 -
fetch_add(n):最常用的计数操作。等价于原子版
++x,但显式控制内存序更稳妥(如fetch_add(1, std::memory_order_relaxed)用于纯计数) -
compare_exchange_weak(expected, desired):实现无锁栈、队列的核心。它不是“读+写”,而是一次性完成“检查+替换”,失败时自动更新
expected,适合循环重试
容易踩坑的关键细节
原子性 ≠ 线程安全的全部。以下三点常被忽略:
-
伪共享(False Sharing):两个高频更新的
std::atomic<int></int>若落在同一缓存行(通常 64 字节),会引发核心间缓存行反复失效。解决方法是用alignas(64)强制隔离,例如:struct alignas(64) Counter { std::atomic<long> hits; };</long> -
内存序误用:默认
seq_cst安全但有开销。跨线程通信时,release写 +acquire读能建立 happens-before 关系;纯计数用relaxed即可;混用会导致重排 bug(如写数据后写标志,却因无序导致另一线程读到旧数据) -
类型限制:并非所有类型都支持无锁原子操作。可用
std::atomic<t>::is_always_lock_free</t>编译期检查。自定义结构体需满足 trivially copyable 且大小适配(通常 ≤ 指针宽度),否则退化为内部加锁,失去原子优势
一个最小可验证示例
下面代码演示两个线程并发递增,使用原子指令后结果恒为 2000:
#include
#include
#include
#include
std::atomic
void worker() {
for (int i = 0; i counter.fetch_add(1, std::memory_order_relaxed);
}
}
int main() {
std::thread t1{worker};
std::thread t2{worker};
t1.join(); t2.join();
std::cout return 0;
}










