內(nèi)存順序

這是從C++11中引深而來的座韵,在C++11標(biāo)準(zhǔn)庫中提供了atomic的原子操作煌张。而其中函數(shù)參數(shù)中有一項(xiàng)是用于指定內(nèi)存順序的。

什么是內(nèi)存順序副签?內(nèi)存順序描述了計(jì)算機(jī)CPU指令訪問內(nèi)存的順序山析。這個(gè)順序和我們通常的代碼順序存在一定的差異堰燎,從而導(dǎo)致在使用多核進(jìn)行多線程編程的情況下可能會(huì)引發(fā)問題。編譯器優(yōu)化和CPU指令都影響這該順序笋轨。比如操作A秆剪,B,C爵政,D四個(gè)CPU操作仅讽。CPU的執(zhí)行順序可能是A->B->C->D或者B->A->C->D等四個(gè)操作的排列組合。同時(shí)不同的CPU間的順序也可能是不一樣的钾挟。

從而在內(nèi)核中引入了barriers(柵欄洁灵,屏障?)掺出,whatever徽千,其本質(zhì)就是一道如同籬笆的隔離機(jī)制,將原本random的內(nèi)存訪問順序變得有組織起來汤锨。這樣做的同時(shí)當(dāng)然是降低了一定性能双抽,畢竟要多執(zhí)行一些操作,但是在并發(fā)編程的情況下闲礼,這是保證數(shù)據(jù)一致性所必須的牍汹。引入barriers之后的操作將變成:memory

barriers->A->B->C->D->memory barriers。使得結(jié)果在多線程并發(fā)的情況下符合預(yù)期柬泽。

C++11標(biāo)準(zhǔn)庫中提供了六種不同的memory order柑贞。即memory_order_relaxed(松散順序)、memory_order_seq_cst(順序一致)聂抢、memory_order_consume、memory_order_acquire棠众、memory_order_release琳疏、memory_order_acq_rel(獲得-釋放順序)。

順序一致:意味著和程序的行為和簡單的順序世界觀是一致的闸拿。舉個(gè)不太恰當(dāng)?shù)睦泳褪窍扔心銧敔斂张危儆心惆郑缓笥心阈禄纭7催^來這個(gè)順序不成立揽趾。順序一致是默認(rèn)的采用的內(nèi)存訪問順序。這也是最嚴(yán)格的一種內(nèi)存順序苛骨。其上的所有多線程并發(fā)看起來就像是一個(gè)線程在執(zhí)行篱瞎,因而其效率損耗也是最大的苟呐。

舉個(gè)例子

如上面的這段代碼,能夠確保assert永遠(yuǎn)不會(huì)發(fā)生俐筋,但是z的值有可能是1也有可能是2牵素。

松散順序:只保證當(dāng)前操作的原子性,不考慮線程間的同步澄者,其他線程可能讀到新值也可能讀到舊值笆呆。比如share_ptr中的引用計(jì)數(shù),只關(guān)心當(dāng)前的應(yīng)用數(shù)量粱挡,而不關(guān)心誰在引用誰在解引用赠幕。

舉個(gè)例子:

上例中assert是有可能觸發(fā)的。因?yàn)閤.load可能讀到false询筏,即使在y已經(jīng)存儲(chǔ)了true的情況下榕堰。a和b是不同的線程,它們之間的內(nèi)存順序可能是不一致的屈留,因此即使b已經(jīng)讀到了y的值為true局冰,它也不一定能夠讀到x的值為true,即使a已經(jīng)將x的值存儲(chǔ)為true了灌危。這個(gè)在x86下比較難調(diào)試出來康二,我實(shí)了很多遍都沒調(diào)試出來。據(jù)說android下可以很容易觸發(fā)勇蝙。

獲取-釋放順序:

獲取-釋放順序是松散順序的進(jìn)步油额,操作在多線程間仍然沒有總的順序,但是引入了一些同步機(jī)制胖烛。

原子載入(load)是獲取操作(memory_order_acquire)

原子存儲(chǔ)(store)是釋放操作(memory_order_release)

原子的讀-修改-寫操作(fetch_add/exchange)是獲取谬莹,釋放或兩者兼?zhèn)?memory_order_acq_rel)

釋放操作與讀取寫入值的獲取操作同步。這意味著翁锡,不同的線程仍然可以看到不同的排序蔓挖,但是這些順序是受到限制的。

傳遞性:如果線程A中的操作發(fā)生于線程B之前馆衔,并且B中的操作發(fā)生于C之前瘟判,則A線程發(fā)生于C之前。傳遞關(guān)系的前提條件是A,B間角溃,B,C間存在同步關(guān)系拷获。

Memory_order_release:

對寫入施加release語義(store),在代碼中這條語句前面的所有讀寫操作都無法被重排(reorder)到這個(gè)操作之后减细。

當(dāng)前線程內(nèi)的所有寫操作匆瓜,對于其它對這個(gè)原子變量進(jìn)行acquire的線程可見

當(dāng)前線程內(nèi)的所有寫操作,對于其它對這個(gè)原子變量進(jìn)行consume的線程可見

Memory_order_acquire:

對于施加acquire(load),在代碼中這條語句后面所有讀寫操作都無法重排到這個(gè)操作之前驮吱。

在這個(gè)原子變量上施加release語義的操作發(fā)生之后茧妒,acquire可以保證讀到所有在release前發(fā)生的寫入。

內(nèi)核中提供了四種內(nèi)存屏障:

Write(或store) memory barriers:

用于保障所有在內(nèi)存屏障之前的STORE操作將比所有屏障后的STORE操作先發(fā)生糠馆。

1.1 write

barriers通常只對stores操作的順序有影響嘶伟,而對loads的操作沒有影響

1.2 通常需要配合read barriers或數(shù)據(jù)依賴共同使用


Data dependency barriers(數(shù)據(jù)依賴屏障)

2.1 數(shù)據(jù)依賴是一種弱讀屏障,用于確保后一個(gè)依賴前一個(gè)操作結(jié)果的操作正確執(zhí)行又碌。如:

*A = 5九昧;

X= *D;

可能的內(nèi)存順序是:

1)STORE *A = 5,x = LOAD *D

2)x = LOAD *D毕匀,STORE *A = 5

而第二種情況將產(chǎn)生錯(cuò)誤铸鹰,因?yàn)樗茸x取寄存器地址再設(shè)置寄存器地址值。(從而導(dǎo)致使用了舊的地址值)

2.2 如果一個(gè)load操作獲取到存儲(chǔ)在另一個(gè)CPU里的指令列表皂岔,那么之道該屏障執(zhí)行完成蹋笼,該序列中所有先于barriers的stores操作對于數(shù)據(jù)依賴屏障之后的任意loads操作都是可見的。


read(或load)memory barriers

3.1 讀內(nèi)存屏障是在數(shù)據(jù)依賴屏障上加上一個(gè)管理躁垛。所有在屏障之前的loads操作將先于屏障之后的loads操作發(fā)生剖毯。

3.2 讀內(nèi)存屏障包含數(shù)據(jù)依賴屏障,因而其包含數(shù)據(jù)依賴的功能教馆。

3.3 讀內(nèi)存屏障通常配合寫內(nèi)存屏障使用


General memory barriers

通用的內(nèi)存屏障確保了所有在屏障之前的LOAD和STORE操作將先于所有位于屏障之后的LOAD和STORE操作發(fā)生逊谋。


一對隱含變量:

Acquire:

Acquire之后的所有內(nèi)存操作將發(fā)生在Acquire操作之后。

Acquire操作包含lock操作土铺,smp_load_acquire胶滋、smp_cond_load_acquire操作。

發(fā)生在Acquire操作之前的內(nèi)存操作可能會(huì)在Acquire完成之后發(fā)生悲敷。

一個(gè)Acquire操作應(yīng)當(dāng)配合Release操作究恤。

一個(gè)給定變量Acquire之后,在其上的所有先于Release的操作將確保已完成后德。



Release操作:

Release確保所有先于Release的內(nèi)存操作將先于Release操作發(fā)生部宿。

Release操作包含unlock和smp_store_release。

Release操作之后的內(nèi)存操作可能會(huì)先于Release操作發(fā)生瓢湃。




Reference:

https://www.zhihu.com/question/24301047/answer/85844428

https://zhuanlan.zhihu.com/p/45566448

https://github.com/torvalds/linux/blob/master/Documentation/memory-barriers.txt

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末理张,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子箱季,更是在濱河造成了極大的恐慌,老刑警劉巖棍掐,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件藏雏,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)掘殴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進(jìn)店門赚瘦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奏寨,你說我怎么就攤上這事起意。” “怎么了病瞳?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵揽咕,是天一觀的道長。 經(jīng)常有香客問我套菜,道長亲善,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任逗柴,我火速辦了婚禮蛹头,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘戏溺。我一直安慰自己渣蜗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布旷祸。 她就那樣靜靜地躺著耕拷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪肋僧。 梳的紋絲不亂的頭發(fā)上斑胜,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天,我揣著相機(jī)與錄音嫌吠,去河邊找鬼止潘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛辫诅,可吹牛的內(nèi)容都是我干的凭戴。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼炕矮,長吁一口氣:“原來是場噩夢啊……” “哼么夫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肤视,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤档痪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后邢滑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腐螟,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了乐纸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衬廷。...
    茶點(diǎn)故事閱讀 38,664評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖汽绢,靈堂內(nèi)的尸體忽然破棺而出吗跋,到底是詐尸還是另有隱情,我是刑警寧澤宁昭,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布跌宛,位于F島的核電站,受9級特大地震影響久窟,放射性物質(zhì)發(fā)生泄漏秩冈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一斥扛、第九天 我趴在偏房一處隱蔽的房頂上張望入问。 院中可真熱鬧,春花似錦稀颁、人聲如沸芬失。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽棱烂。三九已至,卻和暖如春阶女,著一層夾襖步出監(jiān)牢的瞬間颊糜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工秃踩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留衬鱼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓憔杨,卻偏偏與公主長得像鸟赫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子消别,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評論 2 349

推薦閱讀更多精彩內(nèi)容

  • 接著上節(jié) mutex抛蚤,本節(jié)主要介紹atomic的內(nèi)容,練習(xí)代碼地址寻狂。本文參考http://www.cplusplu...
    jorion閱讀 73,601評論 1 14
  • Update Note: 18.07.15 initial version 18.07.26 修訂岁经,改了些明顯的錯(cuò)...
    Quasars閱讀 5,595評論 0 7
  • 參考cppreference參考The C++ Memory Model and Modern Hardware ...
    王偵閱讀 3,144評論 0 3
  • C++ 11 atomic 簡介 Atomic類型是c++11里面引入的一種類型,它規(guī)定了當(dāng)程序的多個(gè)線程同時(shí)訪問...
    EFlql閱讀 7,726評論 1 1
  • 文/孟小滿 (一) “兒子蛇券,你好好考試缀壤,媽媽等你拿到面試通知朽们。” “兒子诉位,你放心,不管要多少錢菜枷,老爸都會(huì)寄給你苍糠,你...
    孟小滿閱讀 1,750評論 94 121