文章目录
- 为什么需要内存序?
- 内存序的分类
- 内存序的作用
- 指令重排序限制
- 跨线程可见性
- 样例分析
- 完全乱序: `memory_order_relaxed`
- 读写同步: `memory_order_acquire` 和 `memory_order_release`
- 一致性 `memory_order_seq_cst`
- 6. 内存序的权衡
- 总结
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 <thread>int main() {std::atomic<int> x(0);std::atomic<int> y(0);std::jthread t1([&]() {y.store(2, std::memory_order_release);x.store(1, std::memory_order_release);});std::jthread t2([&]() {std::cout << x.load(std::memory_order_acquire) << std::endl;std::cout << y.load(std::memory_order_acquire) << std::endl;});return 0;
}
可能的执行结果:
x | y | 可能的执行顺序 |
---|---|---|
1 | 2 | |
1 | 4 | |
3 | 4 |
一致性 memory_order_seq_cst
最严格的内存序, 确保全局顺序一致性.
#include <atomic>
#include <thread>
#include <iostream>std::atomic<int> x(0), y(0);void thread1() {x.store(1, std::memory_order_seq_cst);std::cout << "y: " << y.load(std::memory_order_seq_cst) << std::endl;
}void thread2() {y.store(1, std::memory_order_seq_cst);std::cout << "x: " << x.load(std::memory_order_seq_cst) << std::endl;
}int main() {std::thread t1(thread1), t2(thread2);t1.join();t2.join();return 0;
}
可能的执行步骤
x | y | 可能的执行顺序 |
---|---|---|
3 | 4 | |
3 | 2 |
结果: 输出顺序严格受控, 但性能最低.
6. 内存序的权衡
- 性能优先:
relaxed
内存序提供最高性能, 但不保证顺序和可见性. - 正确性优先:
acquire
/release
是实际编程中常用的同步机制. - 简单实现:
seq_cst
是默认选择, 但代价是性能下降.
总结
- C++ 内存序是原子操作的关键组成部分, 用于平衡性能和同步需求.
- 默认使用
memory_order_seq_cst
, 在性能敏感场景中使用relaxed
或acquire/release
. - 结合实际多线程需求合理选择内存序, 有助于写出高效且安全的代码.