2022-09-20 C++并发编程(二十四)

前言

除了基本的原子操做内存次序,标准库还提供了内存栅栏 std::atomic_thread_fence(),用以灵活改变内存次序。

内存栅栏可以插在原子操作之间,使得程序服从先行关系及同步关系,亦可插入普通操作与原子操作之间,同样可以令其服从内存次序。


一、内存栅栏

顾名思义,内存栅栏就是一种阻止内存乱序的机制,它可以灵活的添加在某些原子操作中间,使得此原子操作在多线程环境服从先行关系和同步关系。

#include <atomic>
#include <cassert>
#include <iostream>
#include <thread>

std::atomic<bool> x, y;
std::atomic<int> z;

//内存栅栏可以在多线程中引入先行关系和同步关系
void writeXThenY()
{
    x.store(true, std::memory_order_relaxed);
    //内存栅栏需要设置在两个需要服从先后次序的位置,因为多线程的同步点是栅栏本身
    std::atomic_thread_fence(std::memory_order_release);
    y.store(true, std::memory_order_relaxed);
}

void writeXThenYUseless()
{
    //错误使用方式,无法使 x 和 y 服从先后次序
    std::atomic_thread_fence(std::memory_order_release);
    x.store(true, std::memory_order_relaxed);
    y.store(true, std::memory_order_relaxed);
}

void readYThenX()
{
    while (!y.load(std::memory_order_relaxed))
    {
    }
    std::atomic_thread_fence(std::memory_order_acquire);
    if (x.load(std::memory_order_relaxed))
    {
        ++z;
    }
}

auto main() -> int
{
    x = false;
    y = false;
    z = 0;
    std::thread a(writeXThenY);
    std::thread b(readYThenX);
    a.join();
    b.join();
    assert(z.load() != 0);
    std::cout << "learnThread_70.cpp" << std::endl;

    return 0;
}

二、通过原子操作令普通变量服从内存次序

内存栅栏插入到普通操作和原子操作之间,也可以约束内存次序,达到通过确立同步关系,强制先行关系的目的。当然,将原子操作的内存次序直接设为释放捕获对 memory_order_release 和 memory_order_acquire 或 memory_order_consume 可达到同样目的。

#include <atomic>
#include <cassert>
#include <iostream>
#include <thread>

bool x = false;
std::atomic<bool> y;
std::atomic<int> z;

void writeXThenY()
{
    //因为 y 的原子操作的同步,使得这里的 X 写入必然先行于 readYThenX() 中的读取
    x = true;
    std::atomic_thread_fence(std::memory_order_release);
    //y 必须是原子操作,否则会引发数据竞争
    y.store(true, std::memory_order_relaxed);
}

void readYThenX()
{
    while (!y.load(std::memory_order_relaxed))
    {
    }
    //通过内存栅栏同步 y 的原子操作, 强制 x 读写的先行关系
    std::atomic_thread_fence(std::memory_order_acquire);
    //由于 y 的同步,确立了 x 读写的先行关系,免除了数据竞争
    if (x)
    {
        ++z;
    }
}

auto main() -> int
{
    x = false;
    y = false;
    z = 0;

    std::thread a(writeXThenY);
    std::thread b(readYThenX);

    a.join();
    b.join();
    assert(z.load() != 0);

    return 0;
}

总结

如果对前文的原子操作内存次序,及先行关系,同步关系等都掌握的话,本文则并不难理解。原子操作部分,介绍就到这里了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不停感叹的老林_<C 语言编程核心突破>

不打赏的人, 看完也学不会.

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值