文章目录
C++ 内存序(Memory Order)定义了多线程环境下对共享内存的操作顺序和可见性规则. 它是 C++ 原子操作(std::atomic
)的重要组成部分, 允许开发者在性能和线程安全之间进行权衡.
为什么需要内存序?
在多线程编程中, 现代 CPU 和编译器会进行指令重排序, 以优化性能. 指令重排序可能导致线程间的操作顺序与程序逻辑不一致, 从而引发数据竞争. C++ 的内存序通过提供一组规则, 确保线程间操作的正确性, 同时尽量减少性能损失.
内存序的分类
C++ 提供了六种内存序, 分别针对不同的同步需求和性能优化:
内存序 | 描述 | 使用场景 |
---|---|---|
memory_order_relaxed | 不保证操作的顺序, 仅保证原子性 | 性能优先, 不需要顺序约束 |
memory_order_consume | 对数据依赖操作可见(目前等价于 acquire ) | 数据依赖同步(罕见使用) |
memory_order_acquire | 确保当前操作之前的读取有效, 阻止后续操作重排到前面 | 多线程读取, 确保前置可见性 |
memory_order_release | 确保当前操作之后的写入有效, 阻止前序操作重排到后面 | 多线程写入, 确保后置可见性 |
memory_order_acq_rel | 同时具备 acquire 和 release 的效果 | 读写同步 |
memory_order_seq_cst | 全局顺序一致性, 最严格的模式 | 默认模式, 简单但性能最低 |
内存序的作用
指令重排序限制
relaxed
: 允许指令完全重排序.acquire
: 禁止读取操作重排到当前操作之前.release
: 禁止写入操作重排到当前操作之后.seq_cst
: 严格禁止所有重排序.
跨线程可见性
acquire
: 确保当前线程可以看到其他线程释放的内存操作.release
: 确保当前线程的操作对其他线程可见.
样例分析
完全乱序: memory_order_relaxed
允许线程独立执行, 不保证顺序或可见性, 仅保证操作原子性.
#include <atomic>
#include <iostream>
#include <thread>
int main() {
std::atomic<int> x(1);
std::atomic<int> y(2);
std::jthread t1([&]() {
y.store(4, std::memory_order_relaxed);
x.store(3, std::memory_order_relaxed);
});
std::jthread t2([&]() {
std::cout << x.load(std::memory_order_relaxed) << std::endl;
std::cout << y.load(std::memory_order_relaxed) << std::endl;
});
return 0;
}
可能有如下 4 种输出:
x | y | 可能的执行顺序 |
---|---|---|
1 | 2 | ![]() |
3 | 2 | ![]() |
1 | 4 | ![]() |
3 | 4 | ![]() |
读写同步: memory_order_acquire
和 memory_order_release
用于线程间同步, 确保操作的可见性和顺序.
#include <atomic>
#include <iostream>
#include <syncstream> // c++20
#include <thread>
int main() {
std::atomic<int> x(1);
std::atomic<int> y(2);
std::jthread t1([&]() {
y.store(4, std::memory_order_release);
x.store(3, std::memory_order_release);
});
std::jthread t2([&]() {
std::osyncstream(std::cout)
<< "x = " << x.load(std::memory_order_acquire) << std::endl;
std::osyncstream(std::cout)
<< "y = " << y.load(std::memory_order_acquire) << std::endl;
});
}
可能的执行结果:
x | y | 可能的执行顺序 |
---|---|---|
1 | 2 | ![]() |
1 | 4 | ![]() |
3 | 4 | ![]() |
一致性 memory_order_seq_cst
最严格的内存序, 确保全局顺序一致性.
#include <atomic>
#include <iostream>
#include <syncstream> // c++20
#include <thread>
std::atomic<int> x(1), y(2);
void thread1() {
x.store(3, std::memory_order_seq_cst);
std::osyncstream(std::cout)
<< "y:" << y.load(std::memory_order_seq_cst) << std::endl;
}
void thread2() {
y.store(4, std::memory_order_seq_cst);
std::osyncstream(std::cout)
<< "x:" << x.load(std::memory_order_seq_cst) << std::endl;
}
int main() {
std::jthread t1(thread1), t2(thread2);
return 0;
}
可能的执行步骤
x | y | 可能的执行顺序 |
---|---|---|
3 | 4 | ![]() |
3 | 2 | ![]() |
1 | 4 | ![]() |
结果: 输出顺序严格受控, 但性能最低.
6. 内存序的权衡
- 性能优先:
relaxed
内存序提供最高性能, 但不保证顺序和可见性. - 正确性优先:
acquire
/release
是实际编程中常用的同步机制. - 简单实现:
seq_cst
是默认选择, 但代价是性能下降.
总结
- C++ 内存序是原子操作的关键组成部分, 用于平衡性能和同步需求.
- 默认使用
memory_order_seq_cst
, 在性能敏感场景中使用relaxed
或acquire/release
. - 结合实际多线程需求合理选择内存序, 有助于写出高效且安全的代码.