原子操作和无锁编程
C++11 开始,线程正式加入标准库,并且一同加入了 <atomic> 无锁编程库。使用原子操作并不代表真的没有互斥锁,无锁编程也不代表真的不用互斥锁进行编程。原子的基本思想是通过尽可能小(有锁或者无锁)的标志,代替对大块数据进行互斥,从而避免死锁和长时间等待。
std::atomic 模板提供了将任意类型包装为原子类型的能力,但是有着一定的要求:
主 std::atomic 模板需要类型 T 满足以下概念(任何若下列任何值为 false 则程序为谬构):
std::is_trivially_copyable<T>::valuestd::is_copy_constructible<T>::valuestd::is_move_constructible<T>::valuestd::is_copy_assignable<T>::valuestd::is_move_assignable<T>::value
标准库内置了四大类型的特化:整数,浮点,指针,布尔,并且前三种种类型都有自己独有的特化成员函数进行数学运算,对于所有 std::atomic 的特化,都有如下成员函数:
operator=存储值于原子对象is_lock_free检查原子对象是否免锁store原子以非原子对象替换原子对象的值load原子获得原子对象的值operator T从原子对象加载值,等价于loadexchange原子替换原子对象的值并获得它先前持有的值compare_exchange_weakcompare_exchange_strong原子比较原子对象与非原子参数的值,若相等则进行交换,若不相等则进行加载wait阻塞线程直至被提醒且原子值更改notify_one提醒至少一个在原子对象上的等待中阻塞的线程notify_all提醒所有在原子对象上的等待中阻塞的线程
对于每个操作,都可以选择不同的 memory_order,某些 memory_order 不能用于某些特定操作。
例如:load 函数是获得操作,那么就不允许使用 release 以及 acq_rel(acquire_release) 顺序;store 是释放操作,那么就不允许 acquire,acq_rel,consume 顺序。
对于整数还有如下特化成员函数:
fetch_add原子将参数加到存储于原子对象的值,并返回先前保有的值fetch_sub原子从存储于原子对象的值减去参数,并获得先前保有的值fetch_and原子进行参数和原子对象的值的逐位与,并获得先前保有的值fetch_or原子进行参数和原子对象的值的逐位或,并获得先前保有的值fetch_xor原子进行参数和原子对象的值的逐位异或,并获得先前保有的值operator++operator++(int)原子自增operator--operator--(int)原子自减operator+=原子加法operator-=原子减法operator&=原子位于operator|=原子位或operator^=原子位异或
浮点数:
fetch_add原子将参数加到存储于原子对象的值,并返回先前保有的值fetch_sub原子从存储于原子对象的值减去参数,并获得先前保有的值operator+=原子加法operator-=原子减法
指针:
fetch_add原子将参数加到存储于原子对象的值,并返回先前保有的值fetch_sub原子从存储于原子对象的值减去参数,并获得先前保有的值operator++operator++(int)原子自增operator--operator--(int)原子自减operator+=原子加法operator-=原子减法