淺談關(guān)于C++memory_order的理解
看了c++并發(fā)編程實(shí)戰(zhàn)的內(nèi)存模型部分后,一直對(duì)memory_order不太懂,今天在知乎發(fā)現(xiàn)了百度的brpc,恰好有關(guān)于原子操作的文檔,感覺解釋的很好。為了加深理解,再次總結(jié)一遍。
在多核編程中,我們使用鎖來避免多個(gè)線程修改同一個(gè)數(shù)據(jù)時(shí)產(chǎn)生的競(jìng)爭(zhēng)條件。但是,鎖會(huì)消耗系統(tǒng)資源,當(dāng)鎖成為性能瓶頸的時(shí)候,就需要使用另一種方法——原子指令。c++11中引入了原子類型atomic。
但僅靠原子指令實(shí)現(xiàn)不了對(duì)資源的訪問控制。這造成的原因是編譯器和cpu實(shí)施了重排指令,導(dǎo)致讀寫順序會(huì)發(fā)生變化,只要不存在依賴,代碼中后面的指令可能會(huì)被放在前面,從而先執(zhí)行它。cpu這么做是為了盡量塞滿每個(gè)時(shí)鐘周期,在單位時(shí)間內(nèi)盡量執(zhí)行更多的指令,從而提高吞吐率。
下面看個(gè)例子:
// thread 1 // ready was initialized to false p.init(); ready = true;
// thread 2 if(ready) { p.bar(); }
線程2在ready為true的時(shí)候會(huì)訪問p,對(duì)線程1來說,如果按照正常的執(zhí)行順序,那么p先被初始化,然后在將ready賦為true。但對(duì)多核的機(jī)器而言,情況可能有所變化:
- 線程1中的ready = true可能會(huì)被cpu或編譯器重排到p.init()的前面,從而優(yōu)先執(zhí)行ready = true這條指令。在線程2中,p.bar()中的一些代碼可能被重排到if(ready)之前。
- 即使沒有重排,ready和p的值也會(huì)獨(dú)立地同步到線程2所在核心的cache,線程2仍然可能在看到ready為true時(shí)看到未初始化的p。
為了解決這個(gè)問題,cpu和編譯器提供了memory fence,讓用戶可以聲明訪存指令的可見性關(guān)系,c++11總結(jié)為以下memory order:
memory order | 作用 |
---|---|
memory_order_relaxed | 無fencing作用,cpu和編譯器可以重排指令 |
memory_order_consume | 后面依賴此原子變量的訪存指令勿重排至此條指令之前 |
memory_order_acquire | 后面訪存指令勿重排至此條指令之前 |
memory_order_release | 前面的訪存指令勿排到此條指令之后。當(dāng)此條指令的結(jié)果被同步到其他核的cache中時(shí),保證前面的指令也已經(jīng)被同步。 |
memory_order_acq_rel | acquare + release |
memory_order_seq_cst | acq_rel + 所有使用seq_cst的指令有嚴(yán)格的全序關(guān)系 |
有了memoryorder,我們可以這么改上面的例子:
// Thread1 // ready was initialized to false p.init(); ready.store(true, std::memory_order_release);
// Thread2 if (ready.load(std::memory_order_acquire)) { p.bar(); }
線程2中的acquire和線程1的release配對(duì),確保線程2在看到ready==true時(shí)能看到線程1 release之前所有的訪存操作。
注意,memory fence不等于可見性,即使線程2恰好在線程1在把ready設(shè)置為true后讀取了ready也不意味著它能看到true,因?yàn)橥絚ache是有延時(shí)的。memory fence保證的是可見性的順序:“假如我看到了a的最新值,那么我一定也得看到b的最新值”。
到此這篇關(guān)于淺談關(guān)于C++memory_order的理解的文章就介紹到這了,更多相關(guān)C++ memory order內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++面試題之?dāng)?shù)a、b的值互換(不使用中間變量)
這篇文章主要介紹了不使用中間變量,C++實(shí)現(xiàn)數(shù)a、b的值互相轉(zhuǎn)換操作,感興趣的小伙伴們可以參考一下2016-07-07QT布局管理詳解QVBoxLayout與QHBoxLayout及QGridLayout的使用
在這篇文章中,你將知道水平布局、垂直布局、網(wǎng)格布局如何輕松上手,以純代碼方式展示。對(duì)齊方式,大小設(shè)置,圖片頭像匹配標(biāo)簽,布局器里面的組件大小隨意切換大小,認(rèn)真看完這篇文章,QT布局管理器熟練使用2022-06-06C/C++使用C語言實(shí)現(xiàn)多態(tài)
這篇文章主要介紹了C/C++多態(tài)的實(shí)現(xiàn)機(jī)制理解的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下,希望能給你帶來幫助2021-08-08C語言實(shí)現(xiàn)數(shù)獨(dú)輔助器(附源碼)
數(shù)獨(dú)是源自瑞士的一種數(shù)學(xué)游戲。是一種運(yùn)用紙、筆進(jìn)行演算的邏輯游戲。本文將利用C語言制作一個(gè)數(shù)獨(dú)輔助器,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-01-01實(shí)例講解C++編程中l(wèi)ambda表達(dá)式的使用
這篇文章主要介紹了C++編程中l(wèi)ambda表達(dá)式的使用實(shí)例,lambda表達(dá)式特性的引入在C++11中可謂千呼萬喚始出來,非常重要,需要的朋友可以參考下2016-01-01C++基于Boost.Asio實(shí)現(xiàn)端口映射器的過程詳解
Boost.Asio 是一個(gè)功能強(qiáng)大的 C++ 庫,用于異步編程和網(wǎng)絡(luò)編程,它提供了跨平臺(tái)的異步 I/O 操作,在這篇文章中,我們將深入分析一個(gè)使用 Boost.Asio 實(shí)現(xiàn)的簡(jiǎn)單端口映射服務(wù)器,文中有詳細(xì)的代碼講解,需要的朋友可以參考下2023-11-11