在 C++ 中,内存序(Memory Order)是多线程编程中原子操作的重要概念,它用于控制原子操作的内存同步行为。C++11 引入了 <atomic> 头文件,提供了内存序来控制多线程环境下的内存访问顺序。
内存序的作用
内存序主要解决两个问题:
-
可见性:一个线程对共享数据的修改何时对其他线程可见
-
顺序性:操作指令的执行顺序如何被其他线程观察
六种内存序
1. memory_order_relaxed
最宽松的顺序约束,只保证原子性,不保证顺序。
cpp
std::atomic<int> x(0);
x.store(1, std::memory_order_relaxed); // 不保证其他线程立即看到这个值
2. memory_order_consume
依赖于该原子操作的后续操作不会被重排序到该操作之前(依赖关系)。
cpp
std::atomic<int*> ptr;
int data;
// 线程1
data = 42;
ptr.store(&data, std::memory_order_consume);
// 线程2
int* p = ptr.load(std::memory_order_consume);
if (p != nullptr) {
// 保证能看到 data = 42
int val = *p;
}
3. memory_order_acquire
用于读操作,保证该操作之后的所有读写不会被重排序到该操作之前。
cpp
std::atomic<bool> flag(false);
int data = 0;
// 线程1
data = 42;
flag.store(true, std::memory_order_release);
// 线程2
while (!flag.load(std::memory_order_acquire));
// 这里保证能看到 data = 42
4. memory_order_release
用于写操作,保证该操作之前的所有读写不会被重排序到该操作之后。
cpp
// 与上面 acquire 配合使用
5. memory_order_acq_rel
同时包含 acquire 和 release 语义,用于读-修改-写操作。
cpp
std::atomic<int> counter(0);
counter.fetch_add(1, std::memory_order_acq_rel);
6. memory_order_seq_cst
最严格的顺序约束(默认),保证所有线程看到相同的操作顺序。
cpp
std::atomic<int> x(0);
x.store(1); // 默认使用 memory_order_seq_cst
典型使用模式
1. Release-Acquire 同步
cpp
std::atomic<bool> ready(false);
int data = 0;
// 线程1(生产者)
data = 42;
ready.store(true, std::memory_order_release);
// 线程2(消费者)
while (!ready.load(std::memory_order_acquire));
// 这里保证能看到 data = 42
2. Release-Consume 同步
cpp
std::atomic<int*> ptr(nullptr);
int value;
// 线程1
value = 100;
ptr.store(&value, std::memory_order_release);
// 线程2
int* p = ptr.load(std::memory_order_consume);
if (p != nullptr) {
// 保证能看到 p 指向的数据
int v = *p; // v = 100
}
3. 自旋锁实现
cpp
class SpinLock {
std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
void lock() {
while (flag.test_and_set(std::memory_order_acquire));
}
void unlock() {
flag.clear(std::memory_order_release);
}
};
性能考虑
-
relaxed:性能最好,但需要谨慎使用
-
seq_cst:性能最差,但最容易理解
-
acquire/release:在性能和正确性之间取得平衡
实用建议
-
优先使用默认的 seq_cst,除非有性能瓶颈
-
理解 happens-before 关系后再使用宽松内存序
-
测试多线程代码,内存序错误很难调试
-
使用现成的同步原语(如 mutex, condition_variable)通常更安全
示例:无锁计数器
cpp
#include <atomic>
#include <thread>
#include <iostream>
class Counter {
std::atomic<int> count{0};
public:
void increment() {
count.fetch_add(1, std::memory_order_relaxed);
}
int get() const {
return count.load(std::memory_order_acquire);
}
};
int main() {
Counter counter;
std::thread t1([&]() {
for (int i = 0; i < 1000000; ++i) {
counter.increment();
}
});
std::thread t2([&]() {
for (int i = 0; i < 1000000; ++i) {
counter.increment();
}
});
t1.join();
t2.join();
std::cout << "Count: " << counter.get() << std::endl;
return 0;
}