一. std::atomic_flag和std::atomic
(一)std::atomic_flag
1. std::atomic_flag是一个bool类型的原子变量,它有两个状态set和clear,对应着flag为true和false。
2. std::atomic_flag使用前必须被ATOMIC_FLAG_INIT初始化,此时的flag为clear状态,相当于静态初始化。
3. 三个原子化操作
(1)test_and_set():检查当前flag是否被设置。若己设置直接返回true,若没设置则将flag置为true ,并返回false。
(2)clear();清除flag标志,即flag=false。
(3)析构函数
4. 和所有atomic类型一样,std::atomic_flag不支持拷贝和赋值等操作。因为赋值和拷贝调用了两个对象,从第一个对象中读值,然后再写入另一个。对于两个独立的对象,这里就有两个独立的操作,合并这两个操作必定不是原子的。
5. std::atomic_flag 类型不提供is_lock_free()。 该类型是一个简单的布尔标志, 并且在这种类型上的操作都是无锁的。但atomic_flag的可操作性不强,导致其应用局限性,还不如std::atomic<bool>。
(二)std::atomic<T>模板类
1. std::atomic_flag是无锁类型的,但是atomic<bool>不一定是lock free的,可以用atomic<T>::is_lock_free()来判断。通常情况下,编译器不会为std::atomic<UDT>生成无锁代码,所有操作使用一个内部锁(UDT为用户自定义类型,如果其类型大小如同int或void*时,大多数平台仍会使用原子指令)。
2. fetch_系列函数返回的是旧值,复合赋值运算返回的是新值,但它们返回的都不是引用类型。因为任何依赖与这个结果的代码都需要显式加载该值。潜在的问题是,结果可能会被其他线程修改。而通过非原子值进行赋值,可以避免多余的加载过程,并且得到实际存储的值。
3. compare_exchange_weak/strong函数可以保证“比较-交换”的原子化。compare_exchange_weak可能失败,即此函数可能与expected值相等的情形下atomic的T值没有替换为disired(atomic值未变)且返回false,这可能发生在缺少单条CAS操作(“比较-交换”指令)的机器上,所以通常使用一个循环中。
bool compare_exchange_strong(T& expected, const T desired) volatile noexcept { if (*this == expected) *this = desired; else expected = *this; return (*this == expected) }