一、内存顺序分类
std::memory_order_relaxed
std::memory_order_consume
std::memory_order_acquire
std::memory_order_release
std::memory_order_acq_rel
std::memory_order_seq_cst
二、作用
| 内存顺序 | 顺序保证 | 同步行为 | 使用场景 |
|---|---|---|---|
std::memory_order_relaxed | 无顺序保证 | 无同步 | 性能最优的情况下,原子操作原子性要求 |
std::memory_order_consume | 确保依赖关系的操作顺序 | 不常用,通常与 acquire 相同 | 对依赖关系有要求,通常忽略 |
std::memory_order_acquire | 确保所有前置操作不重排到 acquire 之后 | 读取同步 | 保证读取数据时,内存顺序正确 |
std::memory_order_release | 确保所有后置操作不重排到 release 之前 | 写入同步 | 确保写数据时,其他线程看到最新值 |
std::memory_order_acq_rel | 结合了 acquire 和 release 的行为 | 读取和写入同步 | 双向同步,读取和写入同步的场景 |
std::memory_order_seq_cst | 严格顺序,保证全局一致的顺序 | 最强的同步机制 | 所有线程必须按程序中的顺序执行 |
三、示例
1.
场景:无序访问,仅关心原子操作的原子性,不关心内存的同步或顺序。
示例:一个简单的线程安全计数器。在这种情况下,我们只需要保证递增操作是原子的,而不关心其他线程是否能够按顺序看到这些递增操作。
#include <iostream>
#include <atomic>
#include <thread>
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.load(std::memory_order_relaxed) << std::endl;
return 0;
}
在这个例子中,我们关心的是 fetch_add 操作的原子性,而不关心其他线程对计数器的可见性或顺序,因此使用 std::memory_order_relaxed。
2.
场景:依赖于一个值的后续操作顺序,但大多数情况下被 std::memory_order_acquire 代替。
示例:如果线程 A 写入一个值,线程 B 基于这个值执行某些操作,std::memory_order_consume 可以确保线程 B 依赖于线程 A 的数据顺序。
#include <iostream>
#include <atomic>
#include <thread>
std::atomic<int> data(0);
std::atomic<bool> ready(false);
void producer() {
data.store(42, std::memory_order_relaxed); // 写数据
ready.store(true, std::memory_order_release); // 通知消费者
}
void consumer() {
while (!ready.load(std::memory_order_acquire)) {} // 等待通知
int value = data.load(std::memory_order_consume); // 依赖于上面写入的数据
std::cout << "Consumed: " << value << std::endl;
}
int main() {
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
return 0;
}
3.
场景:同时具有读取和写入同步的操作。例如,在双端队列中,两个线程需要确保数据读写的同步。
示例:线程 A 和线程 B 通过原子操作读写共享数据,std::memory_order_acq_rel 确保对该数据的写入和读取操作在顺序上保持同步。
#include <iostream>
#include <atomic>
#include <thread>
std::atomic<int> data(0);
void producer() {
data.store(42, std::memory_order_acq_rel); // 同时保证读取和写入同步
}
void consumer() {
int value = data.load(std::memory_order_acq_rel); // 同时保证读取和写入同步
std::cout << "Consumed: " << value << std::endl;
}
int main() {
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
return 0;
}
在这里,std::memory_order_acq_rel 确保 consumer 在读取 data 时,producer 的写入操作已经完成。
4.
场景:全局顺序,用于需要保证所有线程之间操作顺序严格一致的情况。
示例:线程 A 和线程 B 共享一个标志位,当线程 A 设置标志时,线程 B 必须在获取该标志后立即采取行动,并且必须严格按顺序执行。
#include <iostream>
#include <atomic>
#include <thread>
std::atomic<bool> flag(false);
void producer() {
flag.store(true, std::memory_order_seq_cst); // 确保按顺序写入
}
void consumer() {
while (!flag.load(std::memory_order_seq_cst)) {} // 确保按顺序读取
std::cout << "Flag is set!" << std::endl;
}
int main() {
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
return 0;
}
在这个例子中,std::memory_order_seq_cst 确保 consumer 在读取 flag 时,能够看到线程 A 对 flag 的修改,并且保证了全局的操作顺序。
四、总结
std::memory_order_relaxed:只关心原子性,不关心顺序。std::memory_order_consume:用于数据依赖关系,但在实践中常被视为std::memory_order_acquire。std::memory_order_acquire:保证读取的操作不会重排到原子操作之前,确保数据同步。std::memory_order_release:确保写入操作在通知其他线程之前完成。std::memory_order_acq_rel:同时保证读取和写入同步。std::memory_order_seq_cst:保证全局操作顺序的一致性,最强的同步。
1575

被折叠的 条评论
为什么被折叠?



