实际上这就是把我们上文场景2中的flag变量换成了原子量,并用其成员函数进行读写。在这种情况下的逻辑顺序上,step1不会跑到step2后面去,step4不会跑到step3前面去。这样一来,实际上我们就已经保证了当读取到flag为true的时候a一定已经被写入为1了,场景2得到了解决。换一种比较严谨的描述方式可以总结为:
对于同一个原子量,release操作前的写入,一定对随后acquire操作后的读取可见。
这两种内存序是需要配对使用的,这也是将他们放在一起介绍的原因。还有一点需要注意的是:只有对同一个原子量进行操作才会有上面的保证,比如step3如果是读取了另一个原子量flag2,是不能保证读取到a的值为1的。
memory_order_release/memory_order_consume
memory_order_release还可以和memory_order_consume搭配使用。memory_order_release操作的作用没有变化,而memory_order_consume用于load操作,我们简称为consume操作,comsume操作防止在其后对原子变量有依赖的操作被重排到前面去。这种情况下:
-
对于同一个原子变量,release操作所依赖的写入,一定对随后consume操作后依赖于该原子变量的操作可见。
这个组合比上一种更宽松,comsume只阻止对这个原子量有依赖的操作重拍到前面去,而非像aquire一样全部阻止。将上面的例子稍加改造来展示这种内存序,假设flag为一个 atomic特化的bool 原子量,a为一个int变量,b、c各为一个bool变量,并且有如下时序的操作:
step thread A thread B 1 b = true 2 a = 1 3 flag.store(b, memory_order_release) 4 while (!(c = flag.load(memory_order_consume))) 5 assert(a == 1) 6 assert(c == true) 7 assert(b == true)










