C++原子操作的内存序使用及说明(memory ordering)
作者:当归. z Z
内存序主要用于控制多线程环境下的操作可见性和执行顺序,包括无序、消费序、获取、Release、Acq_Rels和_Seq_Cstt等等满足不同MW操作和严格顺序需求,根据不同场景选择合适的内存序,如可有助于优化性能和确保数据一致性
std::memory_order 用于指定原子操作的内存顺序约束,控制多线程环境下的操作可见性和执行顺序。
1.memory_order_relaxed(松散顺序)
特点
- 无同步或顺序约束:仅保证原子操作的原子性,不保证操作顺序。
- 性能最高:适合不需要同步的场景(如计数器)。
示例
std::atomic<int> counter{0};
void increment() {
counter.fetch_add(1, std::memory_order_relaxed); // 无同步保证
}适用场景
- 统计计数、无竞争场景(如性能计数器)。
2.memory_order_consume(消费顺序)
特点
- 比
acquire更弱:仅保证依赖该原子变量的操作不被重排。 - 用于数据依赖链(如指针解引用)。
示例
std::atomic<int*> ptr{nullptr};
int data = 0;
void producer() {
data = 42;
ptr.store(&data, std::memory_order_release); // 发布指针
}
void consumer() {
int* p;
while (!(p = ptr.load(std::memory_order_consume))) { // 消费指针
// 等待
}
assert(*p == 42); // 仅保证 p 依赖的操作有序
}适用场景
- 适用于指针或数据依赖的场景(比
acquire更高效)。
3.memory_order_acquire(获取顺序)
特点
- 与
release配对,确保后续操作不会重排到acquire之前。 - 读取同步:保证看到
release之前的修改。
示例
std::atomic<bool> ready{false};
int data = 0;
void producer() {
data = 42;
ready.store(true, std::memory_order_release); // 发布数据
}
void consumer() {
while (!ready.load(std::memory_order_acquire)) { // 等待 release
// 自旋
}
assert(data == 42); // 保证看到 data = 42
}适用场景
- 锁的获取、线程间数据同步。
4.memory_order_release(释放顺序)
特点
- 与
acquire配对,确保之前的操作不会重排到release之后。 - 写入同步:保证修改对
acquire线程可见。
示例
std::atomic<int> flag{0};
int data = 0;
void producer() {
data = 42;
flag.store(1, std::memory_order_release); // 确保 data = 42 在 flag = 1 之前
}
void consumer() {
while (flag.load(std::memory_order_acquire) != 1) { // 等待 release
// 自旋
}
assert(data == 42); // 保证看到 data = 42
}适用场景
- 锁的释放、线程间数据发布。
5.memory_order_acq_rel(获取-释放顺序)
特点
同时具备 acquire 和 release 语义:
- 如果是读-修改-写(RMW)操作(如
fetch_add),则: - 读取部分 采用
acquire语义(防止后续操作重排到前面)。 - 写入部分 采用
release语义(防止前面操作重排到后面)。
适用于 CAS(Compare-And-Swap)等操作。
示例
std::atomic<int> counter{0};
void increment() {
counter.fetch_add(1, std::memory_order_acq_rel); // 读-修改-写
}适用场景
- 自旋锁、无锁数据结构(如队列、栈)。
6.memory_order_seq_cst(顺序一致性)
特点
- 最强约束:所有
seq_cst操作按全局顺序执行(类似互斥锁)。 - 默认选项(如果不指定内存序,原子操作默认使用
seq_cst)。 - 性能较低,但最安全。
示例
std::atomic<bool> x{false}, y{false};
int z = 0;
void write_x() {
x.store(true, std::memory_order_seq_cst); // 全局可见顺序
}
void write_y() {
y.store(true, std::memory_order_seq_cst); // 全局可见顺序
}
void read() {
while (!x.load(std::memory_order_seq_cst)) {}
if (y.load(std::memory_order_seq_cst)) {
z++; // 保证 x 和 y 的修改顺序一致
}
}适用场景
- 需要严格顺序的场景(如 Dekker 算法、Peterson 锁)。
7.总结
| 内存序 | 作用 | 适用场景 |
|---|---|---|
| relaxed | 仅保证原子性,无顺序约束 | 计数器、无竞争统计 |
| consume | 仅保证依赖该原子变量的操作有序 | 指针发布(较少使用) |
| acquire | 保证后续操作不会重排到它之前(读同步) | 锁获取、数据同步 |
| release | 保证之前操作不会重排到它之后(写同步) | 锁释放、数据发布 |
| acq_rel | 同时具备 acquire 和 release(用于 RMW 操作) | 自旋锁、无锁数据结构 |
| seq_cst | 全局顺序一致(最强约束,性能最低) | 需要严格顺序的算法 |
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
