原子操作
原子操作,是多线程环境下的一个重要概念,是指它是否在共享内存中完成了一个线程相关的单步操作。
- 当一个原子存储作用于一个共享变量时,其他的线程不能监测到这个未完成的修改值。
- 当一个原子加载作用于一个共享变量时,它读取到这个完整的值,就像此时出现了一个单独的时刻。
而非原子加载和存储则不能做到上述两点保证。
为了保证数据的最终一致性,我们可以定义如下规则:
任何时刻两个线程同时操作一个共享变量,当其中一个为写操作时,这两个线程必须使用原子操作。
atomic_flag
atomic_flag
是一种简单的原子布尔类型,只支持两种操作:test-and-set
和 clear
。
构造函数
atomic_flag
只有默认构造函数,拷贝构造函数已被禁用,因此不能从其他的 atomic_flag
对象构造。
1 | atomic_flag() noexcept = default; |
如果在初始化时没有明确使用 ATOMIC_FLAG_INIT
初始化,那么新创建的 atomic_flag
对象的状态是未指定的(unspecified
)(既没有被 set
也没有被 clear
。)另外,atomic_flag
不能被拷贝,也不能 move
赋值。
ATOMIC_FLAG_INIT
:
如果某个
atomic_flag
对象使用该宏初始化,那么可以保证该对象在创建时处于clear
状态。
看一个简单的例子,main() 函数中创建了 10 个线程进行计数,率先完成计数任务的线程输出自己的 ID,后续完成计数任务的线程不会输出自身 ID:
1 |
|
test_and_set
test_and_set()
函数检查 atomic_flag
标志:
如果之前没有被设置过,则设置
atomic_flag
的标志,并返回false
;如果之前
atomic_flag
对象已被设置,则返回true
。
1 | bool test_and_set (memory_order sync = memory_order_seq_cst) volatile noexcept; |
test-and-set
操作是原子(read-modify-write
)的,它到当前操作不受其他线程影响。
参数 sync
的取值如下:
Memory Order 值 | Memory Order 类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
这里涉及到有关 C++ 中内存模型
Memory Order
姿势,先挖个坑,日后单写一篇这个主题的文章。
clear
清除 atomic_flag
对象的标志位,设置 atomic_flag
的值为 false
。
1 | void clear (memory_order sync = memory_order_seq_cst) volatile noexcept; |
清除标志使得下一次调用 test_and_set
返回 false
。
自旋锁
结合 test_and_set()
和 clear()
,atomic_flag
对象可以当作一个简单的自旋锁 使用,请看下例:
1 |
|