atomic
std::atomic 是 C++11 引入的原子操作模板,保证对单个变量的读写和修改操作在多线程环境下是原子的,不会出现数据竞争。它比 mutex 更轻量,适合保护单个标志位、计数器等简单共享变量。
头文件与基本特征
#include <atomic>
| 特征 | 说明 |
|---|---|
| 角色 | 对单个变量提供原子访问 |
| 锁开销 | 通常无锁(lock-free),由硬件原子指令实现 |
| 适用场景 | 标志位、引用计数、自旋锁等简单同步 |
| 不适用 | 保护多个变量的一致性(用 mutex) |
基本用法
#include <atomic>
#include <iostream>
#include <thread>
std::atomic<int> counter(0);
void work() {
for (int i = 0; i < 100000; ++i) {
++counter;
}
}
int main() {
std::thread t1(work);
std::thread t2(work);
t1.join();
t2.join();
std::cout << counter.load() << '\n'; // 200000
return 0;
}
++counter 是原子的,无需加锁,两个线程不会产生数据竞争。
常用操作
| 操作 | 作用 |
|---|---|
load() | 读取当前值 |
store(x) | 写入值 |
exchange(x) | 写入新值并返回旧值 |
compare_exchange_weak(expected, desired) | CAS,值匹配时替换 |
compare_exchange_strong(expected, desired) | CAS 的强一致版本 |
fetch_add(n) / fetch_sub(n) | 原子加减,返回旧值 |
operator++ / operator-- | 原子自增/自减 |
operator+= / operator-= | 原子复合赋值 |
常用场景
标志位
std::atomic<bool> ready(false);
// 线程 A:
ready.store(true);
// 线程 B:
while (!ready.load()) {
// 等待
}
自旋锁
std::atomic_flag lock = ATOMIC_FLAG_INIT;
void critical() {
while (lock.test_and_set()) {
// 自旋等待
}
// 临界区
lock.clear();
}
atomic vs mutex
| 场景 | 推荐 |
|---|---|
| 单个计数器/标志位 | atomic |
| 保护多个变量的一致性 | mutex |
| 临界区包含多行逻辑 | mutex |
| 需要和条件变量配合 | mutex |
使用注意
atomic只保护单个原子操作,两个连续的原子操作之间仍可能被切换。- 不是所有类型都无锁,用
is_lock_free()可以检查。 compare_exchange_weak可能伪失败,需要在循环中使用。- 复杂数据结构不能用
atomic保护,必须用mutex。
小结
std::atomic 是最轻量的并发同步方式,适合标志位、计数器和简单自旋等待。它比 mutex 更快但功能更窄,二者配合 condition_variable 构成了 C++11 并发编程的三大基石。