2022-08-29 C++并发编程(十六)


老林的C语言新课, 想快速入门点此 <C 语言编程核心突破>



前言

上文讲述了最简单的原子标识,本文讲讲 std::atomic<bool> 原子布尔类。相比较 std::atomic_flag ,原子布尔类有更多的操作。


一、操作 std::atomic 对象

原子布尔类不可拷贝构造,不可拷贝赋值,可通过普通布尔类初始化,可接受普通布尔类赋值(不返回引用),可根据 is_lock_free() 函数判断是否为无锁结构:

    //初始化
    std::atomic<bool> atomicBool(true);

    //是否是无锁结构
    std::cout << atomicBool.is_lock_free() << std::endl;

    //原子变量赋值,但只返回值(false)不返回原子变量的引用
    //这个与普通的赋值语义是完全不同的。
    atomicBool = false;

std::atomic<bool> 可以调用 store() 函数进行写操作,用 load() 函数进行读操作,用 exchange() 函数进行读改写操作,返回原有值,并更新,同时可指定内存顺序.

    //读取原子变量
    bool normalBool = atomicBool.load(std::memory_order_acquire);

    //存储原子变量值
    atomicBool.store(true);

    //更新值(false),并返回原值(true)
    normalBool = atomicBool.exchange(false, std::memory_order_acq_rel);

std::atomic<bool> 可以调用 compare_exchange_weak() 及 compare_exchange_strong() 函数实现比较替换操作。

与直接获取原值并更新值的 exchange() 不同,比较替换函数 compare_exchange_weak() 及 compare_exchange_strong() 多了一个与原值比较的期望值参数 expected,是引用形式:

public: bool compare_exchange_weak(
    bool &expected, bool __d, std::memory_order __m = memory_order_seq_cst) noexcept

public: bool compare_exchange_strong(
    bool &expected, bool __d, std::memory_order __m = memory_order_seq_cst) noexcept

当原值与期望值 expected 值一致,则返回true,并更新新值。否则将原值赋予期望值 expected,并返回 false。

weak 版本有一定概率即使原值与期望值 expected 相同,也不会更新既定值,只返回false,即虚假失败(spurious failure),所以通常放在 while 循环中,并且通常效率要高于 strong 版本。

并且比较替换操作还有两个内存顺序参数。

具体细节请看注释:

#include <atomic>
#include <iostream>

auto main() -> int
{
    //初始化
    std::atomic<bool> atomicBool(true);

    //是否是无锁结构
    std::cout << atomicBool.is_lock_free() << std::endl;

    //原子变量赋值,但只返回值(false)不返回原子变量的引用
    //这个与普通的赋值语义是完全不同的。
    atomicBool = false;

    //读取原子变量
    bool normalBool = atomicBool.load(std::memory_order_acquire);

    //存储原子变量值
    atomicBool.store(true);

    //更新值(false),并返回原值(true)
    normalBool = atomicBool.exchange(false, std::memory_order_acq_rel);

    bool expected = false;

    //与期待值 expected 比较,如果相同,更新既定值(true),返回true。
    // weak 版本有一定概率即使与 expected  相同,
    //也可能不会更新既定值,即虚假失败(spurious failure)
    //如果与 expected 不同,用原子变量值更新期待值expected,返回false
    //为了剔除虚假失败,我们一般要联合while循环,但语义也就变了,
    //语义变为与期待值比较,相同则更新既定值,不同则将原值赋值给期待值,更新既定值
    while (!atomicBool.compare_exchange_weak(expected, true) && !expected)
    {
    }

    // strong 版本不会产生虚假失败,其余和 weak 版本相同
    //一般 weak 版本更具效率,因为strong版本有内循环
    //但如果需要语义是与期待值(expected)相同则更改为既定值(false)同时返回true
    //否则与期待值不同,只更改期待值,并返回false,则需要用strong版本。
    //另外,此操作可以设定两个内存次序,因为有成功或失败两种状态
    //成功需读改写次序,失败只能是读次序
    //若不设定内存顺序,则为完全顺序,性能最低。
    bool result = atomicBool.compare_exchange_strong(
        expected, false, std::memory_order_acq_rel, std::memory_order_acquire);

    return 0;
}

总结

原子布尔类操作大都比较简单,只是比较替换操作较难理解,需要多学多练。


老林的C语言新课, 想快速入门点此 <C 语言编程核心突破>


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

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

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

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

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

打赏作者

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

抵扣说明:

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

余额充值