一、标准原子类型atomic types
在C++中,只有在这些原子类型上的操作是语言明确定义的原子操作。其中,std::atomic_flag类型是一个非常简单的无锁布尔标志,在其上的所有操作可总结为:无赋值、无拷贝、无测试、无清除。其余的基础原子类型都可以基于其实现,但由于实现需要内置一个互斥量,所以其不再是无锁原子类型。
二、原子类型的赋值操作
标准原子类型由于没有拷贝构造函数和拷贝赋值运算符,所以其不支持拷贝和赋值。但是,因为可以隐式转化为对应的内置类型,所以这些类型仍旧支持赋值。所支持的赋值操作主要有:
1,load()
2,store()
3,exchange()
4,compare_exchange_weak()
5,compare_exchange_strong()
6,fetch_add()
7,fetch_or()
上述函数的功能描述此处不再过多赘述。
三、多线程环境下原子类型的读取与赋值
对于原子类型来说,赋值操作分为了三个步骤:读(读取当前原子类型的值)-改(更改当前原子类型存储的值)-写(将结果以值形式写入接收变量)。为了获取存储在原子类型变量的值,代码需要执行单独的读操作,从而允许另一个线程在赋值和读取进行的同时修改这个值,这也就为条件竞争带打开了大门。
因此,在多线程环境下,对原子类型的赋值操作需要保证“读改写”操作的原子性,确保该过程不会被其他线程中断,保证线程安全。对于load()/store()等函数来说,其对于单独的某个操作是线程安全的。但是,当存储新值取决于当前值的时候,将“读改写”操作分离进行的做法将会产生数据竞争。
1, 无前置条件下的操作原子变量
当无任何前置条件时,使用load/store等函数对原子变量进行操作是不会产生数据竞争的。因为原子类型类型内部已经保证了这一类操作的原子性。
2, 有前置条件下的操作原子变量
当对原子量的操作存在前置条件时,通常会涉及到先读取原子量的值,再判断该值是否是满足前置条件,最后再根据判断条件决定是否修改原子量的值。在这个过程中,读改写三个阶段已经被切割成了三个部分,在多线程环境下随时可能会因其他线程的介入导致读取到的原子值发生更改。因此,多线程环境下,最好使用compare_exchange_weak/strong()来实现对数据的条件存储,避免因竞态导致的错误数据存储。