先后一致次序(memory_order_seq_cst)
如果程序服从先后一致次序,就简单地把一切事件视为按先后顺序发生,其操作与这种次序保持一致。假设在多线程程序的全部原子类型的实例上,所有的操作都保持先后一致,name它们将按某种特定次序改由单线程执行,则俩个程序的操作毫无区别。
缺点:在弱保序的多处理器计算机上,保序操作会带来很严重的性能损失。
例子:
#include <atomic> #include <thread> #include <assert.h> std::atomic<bool> x,y; std::atomic<int> z; void write_x() { x.store(true,std::memory_order_seq_cst); ⇽--- ① } void write_y() { y.store(true,std::memory_order_seq_cst); ⇽--- ② } void read_x_then_y() { while(!x.load(std::memory_order_seq_cst)); if(y.load(std::memory_order_seq_cst)) ⇽--- ③ ++z; } void read_y_then_x() { while(!y.load(std::memory_order_seq_cst)); if(x.load(std::memory_order_seq_cst)) ⇽--- ④ ++z; } int main() { x=false; y=false; z=0; std::thread a(write_x); std::thread b(write_y); std::thread c(read_x_then_y); std::thread d(read_y_then_x); a.join(); b.join(); c.join(); d.join(); assert(z.load()!=0); ⇽--- ⑤ }
如何理解这个例子呢,关键点还是要理解原子变量。原子变量的意思就是,这个变量load进寄存器和store进内存,在代码里的顺序和真正执行的顺序要保持一致。所以我们先看x, x在write_x(先执行)和read_x_then_y(后执行),这两个函数里出现了。因为在代码的顺序是先write_x中出现,然后read_x_then_y, 所以所有代码的执行顺序是x.store(ture) -》 (在write_x()中), x.load() 和 y.load() -》(在read_x_then_y)中,如果是y先执行。注意每个线程里的代码执行顺序都没有变。这个就是强序带来的。
获取-释放次序(memory_order_cosume, memory_order_acquire, memory_order_release和memory_order_acq_rel)
获取-释放次序比宽松次序严格一些,它会产生一定程度的同步效果,而不会形成先后一致次序的全局总操作序列,在该内存模型中,原子化载入即为获取操作(memory_order_acquire),原子化存储即为释放操作(memory_order_release),而原子化“读改写”操作(像fetch_add()和exchange())则为获取或释放操作,或两者都是(memory_order_acq_rel)。这种内存次序在成对的读写线程之间起到同步作用。释放和获取操作构成同步关系,前者写出的值由后者读取。换言之,若多个线程从获取释放-操作构成同步关系,则其缩减的操作序列可能差异,但差异的程度和方式都受到一定条件的制约。
例子:
宽松情况下
#include <atomic> #include <thread> #include <assert.h> std::atomic<bool> x,y; std::atomic<int> z; void write_x() { x.store(true,std::memory_order_release); } void write_y() { y.store(true,std::memory_order_release); } void read_x_then_y() { while(!x.load(std::memory_order_acquire)); if(y.load(std::memory_order_acquire)) ⇽--- ① ++z; } void read_y_then_x() { while(!y.load(std::memory_order_acquire)); if(x.load(std::memory_order_acquire)) ⇽--- ② ++z; } int main() { x=false; y=false; z=0; std::thread a(write_x); std::thread b(write_y); std::thread c(read_x_then_y); std::thread d(read_y_then_x); a.join(); b.join(); c.join(); d.join(); assert(z.load()!=0); ⇽--- ③ }
上述图中,什么情况都有可能发生。
获取-释放次序下,线程会有同步情况
例子:
#include <atomic> #include <thread> #include <assert.h> std::atomic<bool> x,y; std::atomic<int> z; void write_x_then_y() { x.store(true,std::memory_order_relaxed); ⇽--- ① y.store(true,std::memory_order_release); ⇽--- ② } void read_y_then_x() { while(!y.load(std::memory_order_acquire)); ⇽--- ③以自旋方式等待变量y的值设置为true if(x.load(std::memory_order_relaxed)) ⇽--- ④ ++z; } int main() { x=false; y=false; z=0; std::thread a(write_x_then_y); std::thread b(read_y_then_x); a.join(); b.join(); assert(z.load()!=0); ⑤ }
如果y.load不在循环里,有可能会触发断言。
宽松次序(memory_order_relaxed)
采用宽松次序,name原子类型上的操作不存在同步关系,在单一线程内,同一个变量上的操作依然服从先行关系,但几乎不要求线程间存在任何次序关系。该内存次序的唯一要求是,在一个线程内,对相同变量的访问次序不得重新编排。对于给定的线程,一旦它见到某原子变量在某时刻持有的值,则该线程的后续读操作不可能读取相对更早的值。memory_order_relaxed次序无需任何额外的同步操作,线程间仅存的共有信息是每个变量的改动顺序。
例子:
#include <atomic> #include <thread> #include <assert.h> std::atomic<bool> x,y; std::atomic<int> z; void write_x_then_y() { x.store(true,std::memory_order_relaxed); ⇽--- ① y.store(true,std::memory_order_relaxed); ⇽--- ② } void read_y_then_x() { while(!y.load(std::memory_order_relaxed)); ⇽--- ③ if(x.load(std::memory_order_relaxed)) ⇽--- ④ ++z; } int main() { x=false; y=false; z=0; std::thread a(write_x_then_y); std::thread b(read_y_then_x); a.join(); b.join(); assert(z.load()!=0); ⇽--- ⑤ }
因为在线程write_x_then_y()中,有可能执行顺序是先y.store(true),然后在线程等read_y_then_x执行完之后,再x.store(true)。所以断言可能为失败。
在读《深入理解linux内核》的时候,有了新的理解。所以再回头读一遍,这些接近底层的内容,还是要读底层的代码和相关书籍才能有更深的体会。
标签:std,load,read,C++,---,内存,memory,order From: https://www.cnblogs.com/woodx/p/17378724.html