C++11引入了多線程,同時也引入了一套內存模型。從而提供了比較完善的一套多線程體系。在單線程時代,一切都很簡單。沒有共享數據,沒有亂序執行,全部的指令的執行都是按照預約的時間線。可是也正是由於這個強的同步關係,給CPU提供的優化程度也就相對低了不少。沒法體現當今多核CPU的性能。所以須要弱化這個強的同步關係,來增長CPU的性能優化。性能優化
C++11提供了6種內存模型:多線程
1 enum memory_order{ 2 memory_order_relaxed, 3 memory_order_consume, 4 memory_order_acquire, 5 memory_order_release, 6 memory_order_acq_rel, 7 memory_order_seq_cst 8 }
原子類型的操做能夠指定上述6種模型的中的一種,用來控制同步以及對執行序列的約束。從而也引發兩個重要的問題:app
1.哪些原子類型操做須要使用內存模型?
2.內存模型定義了那些同步語義(synchronization )和執行序列約束(ordering constraints)?性能
原子操做可分爲3大類:優化
讀操做:memory_order_acquire, memory_order_consume
寫操做:memory_order_release
讀-修改-寫操做:memory_order_acq_rel, memory_order_seq_cstui
未被列入分類的memory_order_relaxed沒有定義任何同步語義和順序一致性約束atom
C++11中有3種不一樣類型的同步語義和執行序列約束:spa
1. 順序一致性(Sequential consistency):對應的內存模型是memory_order_seq_cst線程
2.請求-釋放(Acquire-release):對應的內存模型是memory_order_consume,memory_order_acquire,memory_order_release,memory_order_acq_relcode
3.鬆散型(非嚴格約束。Relaxed):對應的內存模型是memory_order_relaxed
下面對上述3種約束作一個大概解釋:
Sequential consistency:指明的是在線程間,創建一個全局的執行序列
Acquire-release:在線程間的同一個原子變量的讀和寫操做上創建一個執行序列
Relaxed:只保證在同一個線程內,同一個原子變量的操做的執行序列不會被重排序(reorder),這種保證也稱之爲modification order consistency,可是其餘線程看到的這些操做的執行序列式不一樣的。
還有一種consume模式,也就是std::memory_order_consume。這個模式主要是引入了原子變量的數據依賴。
Sequential consistency有兩個特性:
1.全部線程執行指令的順序都是按照源代碼的順序;
2.每一個線程所能看到其餘線程的操做的執行順序都是同樣的。
示例代碼:
1 std::string work; 2 std::atomic<bool> ready(false); 3 4 void consumer(){ 5 while(!ready.load()){} 6 std::cout<< work << std::endl; 7 } 8 9 void producer(){ 10 work= "done"; 11 ready=true; 12 }
1. work = "done" sequenced-before ready=true 推導出 work = "done" happens-before ready=true
2. while(!ready.load()){} sequenced-before std::cout<< work << std::endl 推導出 while(!ready.load()){} happens-before std::cout<< work << std::endl
3. ready = true synchronizes-with while(!ready.load()){} 推導出 ready = true inter-thread happens-before while (!ready.load()){},也就推導出ready = true happens-before while (!ready.load()){}
同時由於happens-before關係具備傳遞性,因此上述代碼的執行序列式:
work = "done" happens-before ready = true happens-before while(!ready.load()){} happens-before std::cout<< work << std::endl
關鍵思想是:在同一個原子變量的release操做和acquire操做間同步,同時也就創建起了執行序列約束。
全部的讀和寫動做不能移動到acquire操做以前。
全部的讀和寫動做不能移動到release操做以後。
release-acquire操做在線程間創建了一種happens-before。因此acquire以後的操做和release以前的操做就能進行同步。同時,release-acquire操做具備傳遞性。
示例代碼:
1 std::vector<int> mySharedWork; 2 std::atomic<bool> dataProduced(false); 3 std::atomic<bool> dataConsumed(false); 4 5 void dataProducer(){ 6 mySharedWork={1,0,3}; 7 dataProduced.store(true, std::memory_order_release); 8 } 9 10 void deliveryBoy(){ 11 while( !dataProduced.load(std::memory_order_acquire) ); 12 dataConsumed.store(true,std::memory_order_release); 13 } 14 15 void dataConsumer(){ 16 while( !dataConsumed.load(std::memory_order_acquire) ); 17 mySharedWork[1]= 2; 18 }
1. mySharedWork={1,0,3}; is sequenced-before dataProduced.store(true, std::memory_order_release);
2. while( !dataProduced.load(std::memory_order_acquire) ); is sequenced-before dataConsumed.store(true,std::memory_order_release);
3. while( !dataConsumed.load(std::memory_order_acquire) ); is sequenced-before mySharedWork[1]= 2;
4. dataProduced.store(true, std::memory_order_release); is synchronizes-with while( !dataProduced.load(std::memory_order_acquire) );
5. dataConsumed.store(true,std::memory_order_release); is synchronizes-with while( !dataConsumed.load(std::memory_order_acquire) );
所以dataProducer和dataConsumer可以正確同步。
std::memory_order_consume說的是關於原子變量的數據依賴。
數據依賴有兩種方式:
1. carries-a-dependency-to:若是操做A的結果用於操做B的操做當中,那麼A carries-a-dependency-to(將依賴帶入) B
2. dependency-ordered-before:若是操做B的結果進一步在相同的線程內被操做C使用,那麼A的stor操做(with std::memory_order_release, std::memory_order_acq_rel or std::memory_order_seq_cst)是dependency-ordered-before(在依賴執行序列X以前)B的load操做(with std::memory_order_consume)。
示例代碼:
1 std::atomic<std::string*> ptr; 2 int data; 3 std::atomic<int> atoData; 4 5 void producer(){ 6 std::string* p = new std::string("C++11"); 7 data = 2011; 8 atoData.store(2014,std::memory_order_relaxed); 9 ptr.store(p, std::memory_order_release); 10 } 11 12 void consumer(){ 13 std::string* p2; 14 while (!(p2 = ptr.load(std::memory_order_consume))); 15 std::cout << "*p2: " << *p2 << std::endl; 16 std::cout << "data: " << data << std::endl; 17 std::cout << "atoData: " << atoData.load(std::memory_order_relaxed) << std::endl; 18 }
1. ptr.store(p, std::memory_order_release) is dependency-ordered-before while (!(p2 = ptr.load(std::memory_order_consume)))。由於後面的std::cout << "*p2: " << *p2 << std::endl;將讀取load操做的結果。
2. while (!(p2 = ptr.load(std::memory_order_consume)) carries-a-dependency-to std::cout << "*p2: " << *p2 << std::endl。由於*p2的輸出使用了ptr.load操做的結果
綜上所述,對於data和atoData的輸出是沒有保證的。由於它們和ptr.load操做沒有carries-a-dependency-to關係。同時它們又不是原子變量,這將會致使race condition。由於在同一時間,多個線程能夠訪問data,線程t1(producer)同時會修改它。程序的行爲所以是未定義的(undefined)。
參考:
http://en.cppreference.com/w/cpp/atomic/memory_orderhttp://www.modernescpp.com/