老林的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 语言编程核心突破>