转自html
https://www.cnblogs.com/the-tops/p/6347584.htmlgit
编译时memory moder:https://preshing.com/20120625/memory-ordering-at-compile-time/github
强弱memory order:https://preshing.com/20120930/weak-vs-strong-memory-models/跨域
https://preshing.com/20120710/memory-barriers-are-like-source-control-operations/缓存
acquire/release: https://preshing.com/20120913/acquire-and-release-semantics/安全
看这篇文章:https://www.cl.cam.ac.uk/~pes20/ppc-supplemental/test7.pdfapp
看这篇文章 : https://preshing.com/20140709/the-purpose-of-memory_order_consume-in-cpp11/ui
https://preshing.com/20130823/the-synchronizes-with-relation/atom
https://preshing.com/20130618/atomic-vs-non-atomic-operations/lua
https://github.com/preshing/ConsumeDemo
编译时的:1. volatile关键字: volatile int counter;
2. __asm__ volatile("" : : : "memory");
关于memory_order
这个概念,很是的使人困惑。其关键就是atomic可以保证单个的操做的原子性,但不能保证两个原子操做之间的顺序,这涉及到CPU对缓存刷新时进行的顺序重排。这里看两个简单的例子就能够理解
和咱们平时的理解彻底不同,内存的修改顺序和实际的顺序竟然可能不一致,这就是为何会引入memory_order
这个概念了。
该模型是最强的同步模式,参数表示为std::memory_order_seq_cst,同时也是默认的模型。
-Thread 1- -Thread2-
y = 1if(x.load() ==2) x.store (2); assert (y ==1)
对于上面的例子,即便x和y是不相关的,一般状况下处理器或者编译器可能会对其访问进行重排,可是在seq_cst模式下,x.store(2)以前的全部memory accesses都会happens-before在此次store操做。
另一个角度来讲:对于seq_cst模式下的操做,全部memory accesses操做的重排不容许跨域这个操做,同时这个限制是双向的。
查看下面的典型Acquire/Release的使用例子:
std::atomic<int> a{0}; intb =0; -Thread 1- b = 1; a.store(1, memory_order_release); -Thread 2- while(a.load(memory_order_acquire) !=1)/*waiting*/; std::cout<< b <<'\n';
毫无疑问,若是是seq_cst,那么上面的操做必定是成功的(打印变量b显示为1)。
a. memory_order_release保证在这个操做以前的memory accesses不会重排到这个操做以后去,可是这个操做以后的memory accesses可能会重排到这个操做以前去。一般这个主要是用于以前准备某些资源后,经过store+memory_order_release的方式”Release”给别的线程;
b. memory_order_acquire保证在这个操做以后的memory accesses不会重排到这个操做以前去,可是这个操做以前的memory accesses可能会重排到这个操做以后去。一般经过load+memory_order_acquire判断或者等待某个资源,一旦知足某个条件后就能够安全的“Acquire”消费这些资源了。
这是一个相比Acquire/Release更加宽松的内存模型,对非依赖的变量也去除了happens-before的限制,减小了所需同步的数据量,能够加快执行的速度。
-Thread 1-
n = 1 m = 1 p.store (&n, memory_order_release) -Thread 2- t = p.load (memory_order_acquire); assert( *t == 1&& m ==1); -Thread 3- t = p.load (memory_order_consume); assert( *t == 1&& m ==1);
线程2的assert会pass,而线程3的assert可能会fail,由于n出如今了store表达式中,算是一个依赖变量,会确保对该变量的memory access会happends-before在这个store以前,可是m没有依赖关系,因此不会同步该变量,对其值不做保证。
Comsume模式由于下降了须要在硬件之间同步的数量,因此理论上其执行的速度会比之上面的内存模型块一些,尤为在共享内存大规模数据量状况下,应该会有较明显的差别表现出来。
在这里,Acquire/Consume~Release这种线程间同步协做的机制就被彻底暴露了,一般会造成Acquired/Consume来等待Release的某个状态更新。须要注意的是这样的通讯须要两个线程间成对的使用才有意义,同时对于没有使用这个内存模型的第三方线程没有任何做用效果。
最宽松的模式,memory_order_relaxed没有happens-before的约束,编译器和处理器能够对memory access作任何的re-order,所以另外的线程不能对其作任何的假设,这种模式下能作的惟一保证,就是一旦线程读到了变量var的最新值,那么这个线程将再也见不到var修改以前的值了。
这种状况一般是在须要原子变量,可是不在线程间同步共享数据的时候会用,同时当relaxed存一个数据的时候,另外的线程将须要一个时间才能relaxed读到该值,在非缓存一致性的构架上须要刷新缓存。在开发的时候,若是你的上下文没有共享的变量须要在线程间同步,选用Relaxed就能够了。