第5章 C++内存模型和原子类型操作
5.1 内存模型基础
5.1.1 对象和内存位置
C++程序中所有的数据均是由对象组成的,但是其中有一些不能够派生的类,这些数据在内存中有着严格的放置顺序,这样才保证了开发的正确性,在一个复合结构中我们可以得到:
1.每个变量都是一个对象,包括其他对象的成员
2.每个对象占据至少一个内存位置
3.如int或者char这样的基本类型的变量恰好一个内存位置,无论其大小,即使他们相邻或者是数组的一部分
4.相邻的位域是相同内存位置的一部分
//对于3,4两点,感觉怪怪的
5.1.2 对象、内存位置以及并发
对于并发访问对象,如果多个线程访问的对象没有同一位置的,则没必要考虑由数据竞争导致的写入、读取完整性的问题,只有出现数据竞争时才应该考虑数据的写入、读取之间的完整性。
5.1.3 修改顺序
C++程序中每个对象,都有一个确定的修改顺序,系统中的所有线程必须一致同意此顺序
5.2 C++中的原子操作及类型
原子操作是一个不可分割的操作,这种操作有个特点,要么做完,要么没做完,在其他线程访问的时候,不能够访问到这种过程的中间态。
5.2.1 标准原子类型
标准原子类型有很多
5.2.2 std::atomic_flag上的操作
对于std::atomic_flag的初始化只能使用ATOMIC_FLAG_INIT。
5.2.3 std::atomic<bool>的操作
5.2.4 std::atomic<T*>上的操作:指针算数运算符
5.2.5 标准原子整型的操作
5.2.6 std::atomic<>初级类模板
5.2.7 原子操作的自由函数
对于原子操作并非只有成员函数,当然也存在非成员函数,对于大多数非成员函数只是在原来函数基础上添加atomic_前缀。在有机会指定内存顺序标签的地方,他们有两个变种:一个是没有标签的,一个是添加_explict后缀和额外的参数作为内存顺序的标签。
原子类型的可用操作 | |||||
操作 | atomic_flag | atomic<bool> | atomic<T*> | atomic<integral-type> | atomic<othre-type> |
test_and_set | √ | ||||
clear | √ | ||||
is_lock_free | √ | √ | √ | √ | |
load | √ | √ | √ | √ | |
store | √ | √ | √ | √ | |
exchange | √ | √ | √ | √ | |
compare_exchange_weak | √ | √ | √ | √ | |
compare_exchange_strong | |||||
fetch_add, += | √ | √ | |||
fetch_sub, -= | √ | √ | |||
fetch_or, |= | √ | ||||
fetch_and, &= | √ | ||||
fetch_xor, ^= | √ | ||||
++, -- | √ | √ |
5.3 同步操作和强制顺序
5.3.1 synchronizes-with 关系
5.3.2 happens-before 关系
5.3.3 原子操作的内存顺序
借鉴:知乎如何理解C++11的六种memory_ordered的第一个回答:https://www.zhihu.com/question/24301047/answer/85844428
1. releaxed ordering: 在单线程内,所有原子操作是顺序进行的,按照什么顺序?基本上就是代码顺序(sequenced-before)。这就是唯一的限制了!两个来自不同线程的原子操作是什么顺序?两个字:任意。
2. Release -- acquire: 来自不同线程的两个原子操作顺序不一定?那怎么能限制一下它们的顺序?这就需要两个线程进行一下同步(synchronize-with)。同步什么呢?同步对一个变量的读写操作。线程 A 原子性地把值写入 x (release), 然后线程 B 原子性地读取 x 的值(acquire). 这样线程 B 保证读取到 x 的最新值。注意 release -- acquire 有个牛逼的副作用:线程 A 中所有发生在 release x 之前的写操作,对在线程 B acquire x 之后的任何读操作都可见!本来 A, B 间读写操作顺序不定。这么一同步,在 x 这个点前后, A, B 线程之间有了个顺序关系,称作 inter-thread happens-before.
aquire语义:load 之后的读写操作无法被重排至 load 之前。即 load-load, load-store 不能被重排。
release语义:store 之前的读写操作无法被重排至 store 之后。即 load-store, store-store 不能被重排。