轉自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就能夠了。