C++中的memory_order是C++11引入的原子操作内存顺序属性,用于定义多线程环境下原子操作的执行顺序和内存可见性规则,解决不同线程对共享数据操作的顺序不确定性问题。它和std::atomic类配合使用,让开发者可以在保证程序正确性的同时,根据场景选择合适的内存约束,平衡性能与正确性。

memory_order的核心作用
在多线程场景中,编译器和CPU可能会对指令进行重排序,同时不同CPU核心有各自的内存缓存,这会导致一个线程对共享变量的修改,其他线程不能立即看到,甚至出现操作顺序和代码编写顺序不一致的情况。memory_order就是用来约束这种重排序行为和缓存同步规则的,它告诉编译器和CPU当前原子操作需要什么样的内存可见性保证。
memory_order的常见取值
C++标准定义了6种memory_order取值,每种对应不同的内存顺序约束,具体如下:
| 取值 | 含义 |
|---|---|
| memory_order_relaxed | 宽松顺序,只保证原子操作的原子性,不保证任何内存顺序和可见性约束,性能最高 |
| memory_order_consume | 消费顺序,当前原子操作的结果,对依赖该结果的后续操作可见,目前多数编译器将其当作acquire处理 |
| memory_order_acquire | 获取顺序,搭配release使用,当前操作之后的读写操作,不能被重排序到当前操作之前,且能看到release操作之前的所有写入 |
| memory_order_release | 释放顺序,搭配acquire使用,当前操作之前的读写操作,不能被重排序到当前操作之后,且会被acquire操作看到 |
| memory_order_acq_rel | 获取释放顺序,同时具有acquire和release的特性,用于读-修改-写类型的原子操作 |
| memory_order_seq_cst | 顺序一致顺序,最严格的约束,所有使用该顺序的原子操作全局有一个一致的执行顺序,默认的内存顺序 |
不同memory_order的使用示例
1. memory_order_seq_cst示例
这是默认的内存顺序,所有原子操作全局顺序一致,适合对顺序要求严格的场景:
#include <atomic>
#include <thread>
#include <iostream>
std::atomic<int> x(0);
std::atomic<int> y(0);
void thread1() {
x.store(1, std::memory_order_seq_cst); // 原子写x,顺序一致
y.store(1, std::memory_order_seq_cst); // 原子写y,顺序一致
}
void thread2() {
while (y.load(std::memory_order_seq_cst) == 0) { // 原子读y,顺序一致
// 等待y被修改
}
std::cout << "x的值: " << x.load(std::memory_order_seq_cst) << std::endl; // 原子读x,顺序一致
}
int main() {
std::thread t1(thread1);
std::thread t2(thread2);
t1.join();
t2.join();
return 0;
}
上述代码中,由于使用了顺序一致的内存顺序,线程2看到y为1的时候,一定能看到x已经被修改为1,输出结果一定是1。
2. memory_order_acquire和memory_order_release示例
这两个顺序通常配对使用,实现线程间的同步,比顺序一致的性能更好:
#include <atomic>
#include <thread>
#include <iostream>
std::atomic<int> flag(0);
int data = 0;
void producer() {
data = 42; // 修改共享数据
// release操作,确保data的修改在flag写之前完成,且对acquire可见
flag.store(1, std::memory_order_release);
}
void consumer() {
// acquire操作,确保flag的读之后的操作不会被重排序到前面,且能看到release之前的所有写入
while (flag.load(std::memory_order_acquire) == 0) {
// 等待flag被修改
}
std::cout << "读取到的data值: " << data << std::endl;
}
int main() {
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
return 0;
}
这里producer中flag的release操作,会保证data的修改先于flag的修改被提交,consumer中flag的acquire操作,会保证读到flag为1的时候,一定能看到data已经被修改为42,输出结果一定是42。
3. memory_order_relaxed示例
宽松顺序只保证原子性,不保证可见性顺序,适合不需要同步的场景:
#include <atomic>
#include <thread>
#include <iostream>
std::atomic<int> counter(0);
void increment() {
for (int i = 0; i < 1000; i++) {
// 宽松顺序,只保证自增操作是原子的,不保证顺序
counter.fetch_add(1, std::memory_order_relaxed);
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "最终counter值: " << counter << std::endl;
return 0;
}
这里counter的自增只需要保证原子性,不需要不同线程之间的顺序约束,使用relaxed顺序就可以满足需求,同时性能更好,最终counter的值一定是2000。
memory_order的选择建议
- 如果不确定该用哪种内存顺序,优先选择默认的memory_order_seq_cst,它最安全,不容易出错。
- 如果需要实现两个线程之间的同步,比如生产者消费者场景,优先选择memory_order_acquire和memory_order_release配对使用,性能比顺序一致更好。
- 如果原子操作不需要和其他线程同步,只需要保证自身的原子性,比如计数器场景,可以选择memory_order_relaxed。
- memory_order_consume目前实际使用中较少,多数编译器会将其等同于acquire处理,不建议优先使用。
注意事项
memory_order只对有原子操作的内存顺序有约束,对非原子变量的操作没有约束,因此多线程中共享的非原子变量必须通过原子操作或者互斥锁来保护,不能依赖memory_order来保证非原子变量的可见性。另外,错误的内存顺序选择可能导致程序出现数据竞争和未定义行为,使用前需要充分理解不同取值的含义。
memory_order多线程内存可见性原子操作修改时间:2026-06-27 06:24:39