一、c++中原子变量
原子变量是一种多线程编程中常用的同步机制。它能确保对共 享变量的操作在执行时不会被其他线程的操作干扰,从而避免 竞态条件。
原子变量具备原子性,也就是要么全部完成,要么全部未完 成。c/c++
标准库提供了丰富的原子类型。
std::atomic<T>
is_lock_free
:是否支持无锁操作
store(T desired, std::memory_order order) :用于将指定的值存储到原子对象中;memory_order 为内存序模式。
load(std::memory_order order)
:用于获取原子变量的当前值。
exchange(std::atomic<T>* obj, T desired)
:访问和修改 包含的值,将包含的值替换并返回它前的值。如果替换成功, 则返回原来的值。
compare_exchange_weak(T& expected, T val, memory_order success, memory_order failure) :比较一 个值和一个期望值是否相等,如果相等则将该值替换成一个新 值,并返回 true
;否则不做任何操作并返回
false
。注意, compare_exchange_weak 函数是一个弱化版本的原子操作函 数,因为在某些平台上它可能会失败并重试。如果需要保证严格 的原子性,则应该使用 compare_exchange_strong
函数。
compare_exchange_strong(T& expected, T val, memory_order success, memory_order failure)
fetch_add
fetch_sub
fetch_add
fetch_or
fetch_xor
二、原子变量中的内存序
1.为什么会有内存序问题:编译器优化重排,cpu指令优化重排。
在多线程情况下,操作原子变量时,cpu为避免锁总线会去操作其他变量,但这是无序,可能不符合当前的代码逻辑,所以需要程序员自己规定内存序,定重排规则。
2.规定内存序(同步性、顺序性)
(1).规定多个线程访问同一块内存时的语义;
(2).某个线程对内存地址的更新何时能被其它线程看见;当前核心操作原子变量时何时广播给其它的核心。(同步性)
(3).某个线程对内存地址访问附近可以做怎样的优化;例如i+=5,j+=7,n+=10,依次运行,i为原子变量,当操作i时,cpu不能操作j和n的运算。(顺序性)
三、内存模型
这里所指内存模型对应缓存一致性模型,作用是对同一时间的读写操作进行排序。在不同的 CPU
架构上,这些模型的具体实 现方式可能不同,但是 C++11
帮你屏蔽了内部细节,不用考虑 内存屏障,只要符合上面的使用规则,就能得到想要的效果。 可能有时使用的模型粒度比较大,会损耗性能,当然还是使用 各平台底层的内存屏障粒度更准确,效率也会更高,对程序员 的功底要求也高。
memory_order_relaxed
:松散内存序,只用来保证对原子对 象的操作是原子的,在不需要保证顺序时使用;下图指令序是自己写入代码的顺序
![](https://img-blog.csdnimg.cn/direct/2b1df021bff8486796617e12693b868f.png)
memory_order_release
:
释放操作
,在写入某原子对象时, 当前线程的任何前面的读写操作都不允许重排到这个操作的后面去,并且当前线程的所有内存写入都在对同一个原子
对象进行获
取的其他线程可见;通常与
memory_order_acquire
或 memory_order_consume 配对使用;
![](https://img-blog.csdnimg.cn/direct/424ad1c73a72473a8fd0db1dc6b3f75f.png)
![](https://img-blog.csdnimg.cn/direct/18227d5967304aeb87143887db7fa30e.png)
memory_order_acquire
:
获得操作
,在读取某原子对象时, 当前线程的任何后面的读写操作都不允许重排到这个操作的前面 去,并且其他线程在对同一个原子
对象释放之前的所有内存写入都在当前线程可见;
![](https://img-blog.csdnimg.cn/direct/2287f31889234ef6bb35833093983b68.png)
![](https://img-blog.csdnimg.cn/direct/93b155ef6ef74110bb310a747d960f53.png)
memory_order_consume
:同
memory_order_acquire
类 似,区别是它仅对依赖于该原子变量操作涉及的对象,比如这个操作发生在原子变量 a
上,而
s = a + b
;那
s
依赖于
a
,但
b
不 依赖于 a
;当然这里也有循环依赖的问题,例如:
t = s + 1
,因 为 s
依赖于
a
,那
t
其实也是依赖于
a
的;在大多数平台上,这 只会影响编译器的优化;不建议使用
;
memory_order_acq_rel
:
获得释放操作
,一个
读‐修改‐写
操作同时具有获得语义和释放语义,即它前后的任何读写操作都不允许重排,并且其他线程在对同一个原子对象释放之前的所有内存写入都在当前线程可见,当前线程的所有内存写入都在对同一个 原子对象进行获取的其他线程可见;
memory_order_seq_cst
:
顺序一致性语义
,对于读操作相当 于获得,对于写操作相当于释放,对于读‐修改‐写操作相当于获 得释放,是所有原子操作的默认内存序
,并且会对所有使用此模 型的原子操作建立一个全局顺序
,保证了
多个原子变量
的操作在 所有线程里观察到的操作顺序相同,当然它是最慢的同步模型。
![](https://img-blog.csdnimg.cn/direct/9940023a621c4551b8583f3fb8319dde.png)
分享一个学习链接,有需要的同学可以看一下: