std::atomic_flag
2022年1月4日
字数统计:2499
C++11开始正式加入了多线程库及原子操作,原子操作是无锁并发的基础。所谓原子操作,就是具有原子性的操作:该操作对外不可分割。
std::atomic_flag
std::atomic_flag 是C++唯一保证内部无锁的原子类型,其内部维护一个布尔值。
标准
31.10 Flag type and operations [ atomics.flag]
namespace std
{
struct atomic_flag
{
constexpr atomic_flag () noexcept ;
atomic_flag ( const atomic_flag & ) = delete ;
atomic_flag & operator = ( const atomic_flag & ) = delete ;
atomic_flag & operator = ( const atomic_flag & ) volatile = delete ;
bool test ( memory_order = memory_order :: seq_cst ) const volatile noexcept ;
bool test ( memory_order = memory_order :: seq_cst ) const noexcept ;
bool test_and_set ( memory_order = memory_order :: seq_cst ) volatile noexcept ;
bool test_and_set ( memory_order = memory_order :: seq_cst ) noexcept ;
void clear ( memory_order = memory_order :: seq_cst ) volatile noexcept ;
void clear ( memory_order = memory_order :: seq_cst ) noexcept ;
void wait ( bool , memory_order = memory_order :: seq_cst ) const volatile noexcept ;
void wait ( bool , memory_order = memory_order :: seq_cst ) const noexcept ;
void notify_one () volatile noexcept ;
void notify_one () noexcept ;
void notify_all () volatile noexcept ;
void notify_all () noexcept ;
};
}
The atomic_ flag type provides the classic test-and-set functionality. It has two states, set and clear.
Operations on an object of type atomic_ flag shall be lock-free. The operations should also be address-free.
The atomic_ flag type is a standard-layout struct. It has a trivial destructor.
constexpr atomic_flag::atomic_flag() noexcept;
Effects : Initializes * this to the clear state.
bool atomic_flag_test ( const volatile atomic_flag * object ) noexcept ;
bool atomic_flag_test ( const atomic_flag * object ) noexcept ;
bool atomic_flag_test_explicit ( const volatile atomic_flag * object , memory_order order ) noexcept ;
bool atomic_flag_test_explicit ( const atomic_flag * object , memory_order order ) noexcept ;
bool atomic_flag :: test ( memory_order order = memory_order :: seq_cst ) const volatile noexcept ;
bool atomic_flag :: test ( memory_order order = memory_order :: seq_cst ) const noexcept ;
For atomic_ flag_ test , let order be memory_ order::seq_ cst .
Preconditions : order is neither memory_ order::release nor memory_ order::acq_ rel .
Effects : Memory is affected according to the value of order .
Returns : Atomically returns the value pointed to by object or this .
bool atomic_flag_test_and_set ( volatile atomic_flag * object ) noexcept ;
bool atomic_flag_test_and_set ( atomic_flag * object ) noexcept ;
bool atomic_flag_test_and_set_explicit ( volatile atomic_flag * object , memory_order order ) noexcept ;
bool atomic_flag_test_and_set_explicit ( atomic_flag * object , memory_order order ) noexcept ;
bool atomic_flag :: test_and_set ( memory_order order = memory_order :: seq_cst ) volatile noexcept ;
bool atomic_flag :: test_and_set ( memory_order order = memory_order :: seq_cst ) noexcept ;
Effects : Atomically sets the value pointed to by object or by this to true . Memory is affected according to the value of order . These operations are atomic read-modify-write operations (6.9.2).
Returns : Atomically, the value of the object immediately before the effects.
void atomic_flag_clear ( volatile atomic_flag * object ) noexcept ;
void atomic_flag_clear ( atomic_flag * object ) noexcept ;
void atomic_flag_clear_explicit ( volatile atomic_flag * object , memory_order order ) noexcept ;
void atomic_flag_clear_explicit ( atomic_flag * object , memory_order order ) noexcept ;
void atomic_flag :: clear ( memory_order order = memory_order :: seq_cst ) volatile noexcept ;
void atomic_flag :: clear ( memory_order order = memory_order :: seq_cst ) noexcept ;
Preconditions : The order argument is neither memory_ order::consume, memory_ order::acquire,
nor memory_ order::acq_ rel .
Effects : Atomically sets the value pointed to by object or by this to false . Memory is affected according to the value of order .
void atomic_flag_wait ( const volatile atomic_flag * object , bool old ) noexcept ;
void atomic_flag_wait ( const atomic_flag * object , bool old ) noexcept ;
void atomic_flag_wait_explicit ( const volatile atomic_flag * object ,
bool old , memory_order order ) noexcept ;
void atomic_flag_wait_explicit ( const atomic_flag * object ,
bool old , memory_order order ) noexcept ;
void atomic_flag :: wait ( bool old , memory_order order = memory_order :: seq_cst ) const volatile noexcept ;
void atomic_flag :: wait ( bool old , memory_order order = memory_order :: seq_cst ) const noexcept ;
For atomic_ flag_ wait, let order be memory_ order::seq_ cst . Let flag be object for the non-member functions and this for the member functions.
Preconditions : order is neither memory_ order::release nor memory_ order::acq_ rel .
Effects : Repeatedly performs the following steps, in order:
(15.1) — Evaluates flag->test(order) != old.
(15.2) — If the result of that evaluation is true, returns.
(15.3) — Blocks until it is unblocked by an atomic notifying operation or is unblocked spuriously.
Remarks : This function is an atomic waiting operation (31.6).
void atomic_flag_notify_one ( volatile atomic_flag * object ) noexcept ;
void atomic_flag_notify_one ( atomic_flag * object ) noexcept ;
void atomic_flag :: notify_one () volatile noexcept ;
void atomic_flag :: notify_one () noexcept ;
Effects : Unblocks the execution of at least one atomic waiting operation that is eligible to be unblocked
(31.6) by this call, if any such atomic waiting operations exist.
Remarks : This function is an atomic notifying operation (31.6).
void atomic_flag_notify_all ( volatile atomic_flag * object ) noexcept ;
void atomic_flag_notify_all ( atomic_flag * object ) noexcept ;
void atomic_flag :: notify_all () volatile noexcept ;
void atomic_flag :: notify_all () noexcept ;
Effects : Unblocks the execution of all atomic waiting operations that are eligible to be unblocked (31.6)
by this call.
Remarks : This function is an atomic notifying operation (31.6).
31.10标志类型和操作 [ atomics.flag]
namespace std
{
struct atomic_flag
{
constexpr atomic_flag () noexcept ;
atomic_flag ( const atomic_flag & ) = delete ;
atomic_flag & operator = ( const atomic_flag & ) = delete ;
atomic_flag & operator = ( const atomic_flag & ) volatile = delete ;
bool test ( memory_order = memory_order :: seq_cst ) const volatile noexcept ;
bool test ( memory_order = memory_order :: seq_cst ) const noexcept ;
bool test_and_set ( memory_order = memory_order :: seq_cst ) volatile noexcept ;
bool test_and_set ( memory_order = memory_order :: seq_cst ) noexcept ;
void clear ( memory_order = memory_order :: seq_cst ) volatile noexcept ;
void clear ( memory_order = memory_order :: seq_cst ) noexcept ;
void wait ( bool , memory_order = memory_order :: seq_cst ) const volatile noexcept ;
void wait ( bool , memory_order = memory_order :: seq_cst ) const noexcept ;
void notify_one () volatile noexcept ;
void notify_one () noexcept ;
void notify_all () volatile noexcept ;
void notify_all () noexcept ;
};
}
atomic_ flag类型提供传统的test-and-set功能。它有两个状态,设置(set)和清除(clear)。
对atomic_ flag类型的成员的任何操作都应是无锁的。这些操作也应是地址安全的(address-free)。
atomic_ flag类型是一个标准布局结构体,它有一个平凡的析构器。
constexpr atomic_flag::atomic_flag() noexcept;
作用 : 初始化 * this 是将值设置为清除(clear)。
bool atomic_flag_test ( const volatile atomic_flag * object ) noexcept ;
bool atomic_flag_test ( const atomic_flag * object ) noexcept ;
bool atomic_flag_test_explicit ( const volatile atomic_flag * object , memory_order order ) noexcept ;
bool atomic_flag_test_explicit ( const atomic_flag * object , memory_order order ) noexcept ;
bool atomic_flag :: test ( memory_order order = memory_order :: seq_cst ) const volatile noexcept ;
bool atomic_flag :: test ( memory_order order = memory_order :: seq_cst ) const noexcept ;
对于 atomic_ flag_ test ,假设 order 是 memory_ order::seq_ cst 。
先决条件 : order 既不是 memory_ order::release 也不是 memory_ order::acq_ rel 。
作用 : 内存根据 order 的值被改变。
结果 : 原子的返回指向 object 或者 this 的值。
bool atomic_flag_test_and_set ( volatile atomic_flag * object ) noexcept ;
bool atomic_flag_test_and_set ( atomic_flag * object ) noexcept ;
bool atomic_flag_test_and_set_explicit ( volatile atomic_flag * object , memory_order order ) noexcept ;
bool atomic_flag_test_and_set_explicit ( atomic_flag * object , memory_order order ) noexcept ;
bool atomic_flag :: test_and_set ( memory_order order = memory_order :: seq_cst ) volatile noexcept ;
bool atomic_flag :: test_and_set ( memory_order order = memory_order :: seq_cst ) noexcept ;
作用 : 原子的修改指向 object 或者 this 的值为 true 。内存根据 order 的值被改变。这些操作原子的进行read-modify-write操作 (6.9.2)。
结果 : 原子的,对象的值在影响之前。
void atomic_flag_clear ( volatile atomic_flag * object ) noexcept ;
void atomic_flag_clear ( atomic_flag * object ) noexcept ;
void atomic_flag_clear_explicit ( volatile atomic_flag * object , memory_order order ) noexcept ;
void atomic_flag_clear_explicit ( atomic_flag * object , memory_order order ) noexcept ;
void atomic_flag :: clear ( memory_order order = memory_order :: seq_cst ) volatile noexcept ;
void atomic_flag :: clear ( memory_order order = memory_order :: seq_cst ) noexcept ;
先决条件 : order 既不是 memory_ order::consume 也不是 memory_ order::acquire 或 memory_ order::acq_ rel 。
作用 : 原子的修改指向 object 或者 this 的值为 false 。内存根据 order 的值被改变。
void atomic_flag_wait ( const volatile atomic_flag * object , bool old ) noexcept ;
void atomic_flag_wait ( const atomic_flag * object , bool old ) noexcept ;
void atomic_flag_wait_explicit ( const volatile atomic_flag * object ,
bool old , memory_order order ) noexcept ;
void atomic_flag_wait_explicit ( const atomic_flag * object ,
bool old , memory_order order ) noexcept ;
void atomic_flag :: wait ( bool old , memory_order order = memory_order :: seq_cst ) const volatile noexcept ;
void atomic_flag :: wait ( bool old , memory_order order = memory_order :: seq_cst ) const noexcept ;
对于atomic_ flag_ wait,假设 order 是 memory_ order::seq_ cst 。假设 flag 是非成员函数的对象和 this 是成员函数的。
先决条件 : order 既不是 memory_ order::release 也不是 memory_ order::acq_ rel 。
作用 : 重复执行下列步骤,依据order:
(15.1) — 评估 flag->test(order) != old。
(15.2) — 如果结果为true,返回。
(15.3) — 锁定,直到它被一个原子通知操作解除锁定或被虚假地解除锁定。
注释 : 这个函数是一个原子的等待操作 (31.6)。
void atomic_flag_notify_one ( volatile atomic_flag * object ) noexcept ;
void atomic_flag_notify_one ( atomic_flag * object ) noexcept ;
void atomic_flag :: notify_one () volatile noexcept ;
void atomic_flag :: notify_one () noexcept ;
作用 : 解除至少一个原子等待操作的执行,如果有任何这样的原子等待操作存在,该调用有资格被解除封锁(31.6)。
注释 : 这个函数是一个原子的通知操作 (31.6)。
void atomic_flag_notify_all ( volatile atomic_flag * object ) noexcept ;
void atomic_flag_notify_all ( atomic_flag * object ) noexcept ;
void atomic_flag :: notify_all () volatile noexcept ;
void atomic_flag :: notify_all () noexcept ;
作用 : 解除所有有资格被此调用解锁的原子等待操作的执行 (31.6)。
注释 : 这个函数是一个原子的通知操作 (31.6)。
注释
根据cppreference的文章成员函数 ,volatile修饰成员函数的目的是对this* 进行限定,类似于const修饰的成员函数只能调用该类的const修饰的成员函数。
根据cppreference的文章destructor ,平凡析构代表着,析构函数非虚且不是用户提供的,平凡析构函数是不进行任何动作的析构函数。有平凡析构函数的对象不要求delete表达式,并可以通过简单地解分配其存储进行释放。
关于std::memory_ order的内容将在后半部分讲述。
总结
目的
std::atomic_ flag是原子类型的最小概念的实现,默认值为clear(false)。
在C++20之前,std::atomic_ flag必须使用ATOMIC_ FLAG_ INIT这个宏进行初始化,但是标准委员会意识到声明std::atomic_ flag而不进行初始化没有任何意义,并且增加了无效代码,所以C++20开始,std::atomic_ flag被默认初始化为清除,通过构造函数 (D.25.1)。
C++11后,C++20之前,std::atomic_ flag只有3类成员函数,由于std::atomic_ flag实际上为布尔值,所以这三个函数的参数只有std::memory_ order ,不需要传入参数,因此std::atomic_ flag是一个非常轻量的标志:
bool test(memory_ order) :返回当前值
bool test_ and_ set(memory_ order) :将值设置为true并返回先前值
void clear(memory_ order):将值设置为false
C++并发编程实战第二版中提出了一种使用std::atomic_ flag的自旋锁类型:
class spinlock_mutex
{
std :: atomic_flag flag {};
public :
void lock ()
{
while ( flag . test_and_set ( std :: memory_order_acquire ));
}
void unlock ()
{
flag . clear ( std :: memory_order_release );
}
};
这个非常基础的自旋锁可以配合std::lock_ guard来使用:
锁作为类成员在构造时被初始化
在对类的其他成员进行修改前,尝试上锁
上锁成功后执行修改操作
修改完成后解锁
由于test_ and_ set会返回之前的锁的状态,因此在while循环中,只有真正解锁(clear)才会停止循环,由于test_ and_ set具有原子性,所以test_ and_ set并不会破坏锁原本的状态。
遗憾的是,C++并发编程实战写于2018年并在2019年2月出版,而C++20恰好对std::atomic_ flag做了改进:加入了wait,notify_ one,notify_ all函数,增强了std::atomic_ flag的功能。
class wait_mutex
{
std :: atomic_flag flag {};
public :
void lock ()
{
while ( flag . test_and_set ( std :: memory_order_acquire )){
flag . wait ( flag . test ( std :: memory_order_acquire ), std :: memory_order_acquire );
}
}
void unlock ()
{
flag . clear ( std :: memory_order_release );
flag . notify_one ();
}
};
改进版的代码中添加了一个wait语句来让while循环等待,在解锁时发送停止等待的通知。
参考
若无特殊声明,本人原创文章以
CC BY-SA 4.0许可协议
提供。
本站不欢迎非搜索引擎类,非个人学习类爬虫;严禁将文章直接爬取至其他站点。
若看到此条消息,说明你正在访问的网站可能是垃圾二手转载网站。
为了获得更好的浏览体验,请访问唯一原始网站:mysteriouspreserve[dot]com或blog[dot]bizwen[dot]com。