memory_order
类型 | 效果 | 作用范围 |
---|---|---|
memory_order_acquire | 当前线程的所有读写无法通过重排放到这个加载(load) 之前,其他线程对相同原子变量的写操作对当前线程也是可见的。 | load |
memory_order_release | 当前线程的所有读写无法重排到这个存储(store) 之后,所有当前线程的写操作对于其他acquire 相同原子变量的线程都是可见的 | store |
memory_order_acq_rel | 组织读写重排到原子变量load 之前或store 之后,对这个原子变量的读写对各个线程都是可见的 | read-modify-write |
memory_order_seq_cst | 这个内存序在读、写、读-改-写的操作和上面的三种一样,但是附加一个总体序存在,这个总体序在让各个线程观察到相同的修改。 | load/store/read-modify-write |
形式化描述
Sequenced-before
在相同的线程,求值A可能sequenced-before求值B,就像求值顺序中描述的一样。
Carries dependency
在相同线程中,如果下列条件成立,那么求值A sequenced-before求职B可能会同时carry a dependency into B,也就是B依赖于A:
- A的值作为B的操作,除以下情形:
- 如果B是一个对std::kill_dependency的调用
- 如果A是原生的&&, ||, ?: 或者 , 操作的左操作数;
- A写了一个数值对象M,B从数值对象M中读取;
- B依赖于X,而X依赖于A。
Modification order
对于指定atomic变量在全局序(total order)
下的所有修改都是特指于该atomic变量的。
以下四点需求对于所有原子操作都是有保证的:
-
Write-write coherence:如果求值A(A修改了一些原子的M,写操作)happens-before求值B(B也修改一些原子的变量M),那么在M的修改顺序上A的效果比B出现地早。
-
Read-read coherence:
如果满足以下条件
则B中的值不是X传入的也是X之后side effect
Y。
- Read-write coherence:
如果A对于M的读操作happens-before B对于M的写操作,那么A获得的值来自于B之前对于M的写操作side effect
X。
- Write-read coherence:
如果一个对于原子对象M的side effect
X(一个写操作)happen-before一个对于相同原子对象M的值计算操作B(一个读操作),那么B的求值将会从side effect
X取得这个值或者从一个在修改M值的操作上位于X之后的side effect
Y。
Release sequence
在一个release操作A被在原子对象M上演绎以后,满足以下条件的对M的modification order
最长连续子序列被称为以A开头的release sequence.
- 演绎了A的线程演绎(peformed)的写. (until C++20)
- 其他线程的所有对M的
read-modify-write
操作。
Release operation
通过memory_order_release或者更强的原子变量存储操作被称为release操作。互斥量(Mutex)的unlock()操作也是一个release操作。需要注意的是,std::atomic_thread_fence会强制增加一个比release操作更强的同步需求.
Synchronizes with
如果在线程A中一个原子的store
是一个release
操作,在B线程中同一个原子变量的load
是一个acquire
操作,并且B中读到的值是A中写入的值,那么称为线程Asynchronizes-with
线程B。
Dependency-order berfore
满足以下条件即可称为A的求值dependency-before B的求值
:
-
A在原子的M上演绎(perform)了一个release操作,并且另外一个线程中,B在相同原子变量M上演绎了一个consume操作,并且B读取的值是A写的(或者被A开头的
release sequence
写的, until C++ 20) -
A
dependency-order before
X,并且Xcarries a dependency into
B.
Inter-thread happens-before
在两个线程间,如果满足以下条件之一则称为A inter-thread happens before B
:
- A synchronizes-with B;
- A dependency-order before B;
- A synchronizes-with 一个求值X,并且X是sequenced-before B的;
- A sequenced-before一个求值X,并且X
inter-thread happens-before
B; - A inter-thread happens-before求值X,并且X inter-thread happens-before B;
Happens-before
不考虑多线程,当满足以下条件之一时,称为求值A happens-before求值B
:
- A sequenced-before B;
- A inter-thread happens before B;
应用要求,如果必要的话通过介绍额外的同步,保证happens-before
是非循环的。
如果一个求值修改某内存位置,另外一个读或者写了同样内存地址的,并且其中至少有一个是非原子的,那么除非两个求值之间存在happens-before
的操作,否则结果是未定义的。
Simply happens-before(since C++ 20)
不考虑多线程,当满足以下条件之一是被称为求值A simply happens-before 求值B
:
- A sequenced-before B;
- A synchronizes-with B;
- A simply happens-before X, 并且X simply happens-before B;
注意:如果没有consume operation,那么simply happens-before和happens-before是一个意思;
Strongly happens-before
不考虑多线程,满足以下条件之一时,称为求值A strongly happens-before 求值B
:
until C++20(译者注:在20之前strongly和simply的规则看上去一模一样)
- A sequnced-before B.
- A synchronizes-with B;
- A strongly happens-before X, 并且X strongly happens-before B;
since C++20
- A sequenced-before B;
- A synchronizes with B,并且A和B是序列连续(sequentially consistent)的原子操作;
- A sequenced-before X,并且X simply happens-before Y,并且Y sequenced-before B;
- A strongly happens-before X,且 X strongly happens-before B.
如果A strongly happens-before B,那么A在全局上求值都在B之前;
strongly happens-before剔除了consume operation;
Visible side-effects
当一下条件全部满足时,数量M上的side-effect A
(write)在计算B(read)的角度是可见的:
- A happens-before B;
- 在A和B之间没有其他的
side-effect X
.
如果A is visible to the value computation B.那么这个对于M的side-effect
的遵从modification-order的最长邻近子集被称为the visible sequence of side-effects
.由B决定的M值,应该是这些side-effects存(store)进去的。
注意:inter-thread synchronization 归结为:1、阻止data race(通过建立happen-before关系);2、定义在何种情况下哪些side-effects变为可见。