C++ 原子操作提供 6种内存序(memory_order),分别控制编译器和 CPU 如何对内存操作进行重排序优化,确保并发程序在多线程之间的行为可控、正确。
所有内存序(memory_order)
C++ 中 std::memory_order
的 6 种选项如下:
枚举值 | 中文名 | 简要说明 |
---|---|---|
memory_order_relaxed | 放松序 | 不保证任何顺序,只保证原子性 |
memory_order_consume | 消费序(已废弃趋势) | 数据依赖同步,仅限依赖关系(不推荐使用) |
memory_order_acquire | 获取序 | 保证本线程之后的操作不会被重排到原子操作前 |
memory_order_release | 释放序 | 保证本线程之前的操作不会被重排到原子操作后 |
memory_order_acq_rel | 获取 + 释放序 | 同时保证 acquire + release 的效果,适用于 read-modify-write 操作 |
memory_order_seq_cst | 顺序一致性(默认) | 最强的保证:所有线程中看起来像是全局有序执行 |
简明图解理解(线程内操作顺序)
-
relaxed
: 无序(性能最好,但易出错) -
acquire
: 不能把后面的操作移到前面 -
release
: 不能把前面的操作移到后面 -
acq_rel
: 二者兼具 -
seq_cst
: 所有线程观察到的执行顺序一致
常见用法示例对比
默认顺序一致性 memory_order_seq_cst
#include <atomic>
std::atomic<int> x{0}; // 初始化为 0
x.store(10); // store (默认 seq_cst)
int v = x.load(); // load (默认 seq_cst)
顺序一致性:多线程中所有原子操作全局有序,易于理解但开销较大。
放松序 memory_order_relaxed
#include <atomic>
std::atomic<int> x{0}; // 初始化为 0
// 线程 A
x.store(10, std::memory_order_relaxed); // 原子写,不保证顺序
// 线程 B
int value = x.load(std::memory_order_relaxed); // 原子读,不保证看到更新
获取 / 释放语义:典型锁实现方式
#include <atomic>
std::atomic<bool> flag{false}; // 默认未设置
// 线程 A:写线程
data = 123; // 假设 data 是共享变量
flag.store(true, std::memory_order_release); // 发布 data 已准备好
// 线程 B:读线程
if (flag.load(std::memory_order_acquire)) {
// 这里看到 flag == true
// 根据 acquire 语义:保证看到的 data = 123
std::cout << data << std::endl;
}
这就是经典的 “写线程 release,读线程 acquire” 搭配,确保读线程看到完整写入的数据。
atomic_flag
使用场景建议的 memory_order
操作类型 | 推荐的 memory_order |
---|---|
flag.test_and_set | std::memory_order_acquire |
flag.clear | std::memory_order_release |
小结
序名 | 用途 | 是否有序 | 性能 | 说明 |
---|---|---|---|---|
relaxed | 最快的,不同步 | ❌ | 🔥🔥🔥 | 仅原子性,无顺序 |
acquire | 用于加锁 | ✅(后有序) | 🔥🔥 | 加锁读 |
release | 用于解锁 | ✅(前有序) | 🔥🔥 | 解锁写 |
acq_rel | 读写同时 | ✅ | 🔥 | 用于 fetch_add 等 |
seq_cst | 最强顺序保证 | ✅✅✅ | 🐢 | 默认值,全局有序 |
consume | 基本弃用 | ❓ | ❓ | 不推荐 |