原子操作的内存顺序

一、内存顺序分类

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:保证全局操作顺序的一致性,最强的同步。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值