std::atomic 是 C++11 提供的无锁原子操作工具,用于多线程中安全读写简单变量(如 int、bool、指针),依赖 CPU 原子指令而非互斥锁;适用于计数器、状态标志、单生产者-单消费者队列索引等场景,但仅保证单操作原子性,不保障多变量逻辑一致性。

std::atomic 是 C++11 引入的核心工具,用来安全地在多线程中读写共享变量,避免数据竞争,是无锁编程的基础。它不依赖互斥锁(mutex),而是靠底层 CPU 指令(如 CAS、load/store with memory ordering)保证操作的原子性。
什么时候该用 std::atomic?
当你有一个简单变量(如 int、bool、指针),多个线程要并发读写,且操作本身足够“小”(比如加 1、赋值、比较交换),又不想上锁——这时候 atomic 就很合适。
- 计数器(如引用计数、请求统计)
- 状态标志(如 running = true/false)
- 单生产者-单消费者队列中的头尾索引
- 实现自旋锁、信号量等基础同步原语
⚠️ 注意:atomic 不等于“万能线程安全”。它只保单个操作原子,不保多个 atomic 变量之间的逻辑一致性。比如“先读 A 再写 B”不是原子的,仍需额外同步。
基本用法:声明、读、写、修改
以 int 为例:
立即学习“C++免费学习笔记(深入)”;
std::atomiccounter{0}; // 初始化为 0 counter.store(42); // 原子写(等价于 counter = 42) int x = counter.load(); // 原子读(等价于 x = counter) int y = counter++; // 原子后增(返回旧值) counter.fetch_add(3); // 原子加 3,返回旧值 counter.compare_exchange_weak(expected, desired); // CAS:若当前值==expected,则设为desired,返回true;否则把当前值写入expected,返回false
常见成员函数还有:exchange()(原子替换并返回旧值)、fetch_sub()、fetch_and() 等。所有操作默认使用 memory_order_seq_cst(最强顺序,最安全也稍慢)。
内存序(memory order)怎么选?
内存序控制编译器重排和 CPU 乱序执行的边界,影响性能与正确性。初学者建议先全用默认(seq_cst),稳定后再优化。
-
memory_order_relaxed:只保证原子性,不约束前后指令顺序。适合计数器、时间戳等无需同步语义的场景。 -
memory_order_acquire:用于读操作,保证它之后的读写不被重排到它前面(进入临界区)。 -
memory_order_release:用于写操作,保证它之前的读写不被重排到它后面(退出临界区)。 -
memory_order_acq_rel:读-修改-写操作(如 fetch_add)常用,兼具 acquire 和 release。 -
memory_order_seq_cst:全局顺序一致,所有线程看到的操作顺序相同。C++ 默认,最易理解。
例子:用 atomic 实现自旋锁
struct spinlock {
std::atomic locked{false};
void lock() {
while (locked.exchange(true, std::memory_order_acquire)) {
// 自旋等待
}
}
void unlock() {
locked.store(false, std::memory_order_release);
}
};
注意事项和常见坑
不能对 atomic 对象取地址或 memcpy —— 它可能包含对齐填充或内部状态,行为未定义。
不支持浮点类型直接原子操作(C++20 起部分支持) —— 如需原子 float,可用 std::atomic + union 或 bit_cast 模拟(需谨慎处理 NaN/符号)。
结构体不能直接 atomic,除非是 trivially copyable 且满足对齐要求 —— 大多数自定义 struct 需用 mutex 保护,或用 std::atomic_ref(C++20)包装已存在变量。
compare_exchange_weak 可能虚假失败 —— 在循环中使用,不要假设失败就代表值变了。
基本上就这些。atomic 是无锁编程的起点,但不是终点。真正复杂的无锁数据结构(如无锁栈、队列)需要深入理解内存模型和算法设计。先从计数器、标志位练起,再逐步挑战 CAS 循环和 ABA 问题应对——不复杂但容易忽略细节。










