国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

淺談Volatile與多線程

2019-11-11 00:03:43
字體:
來源:轉載
供稿:網友
淺談Volatile與多線程
最近看的比較雜,摘了一些人的筆記!隨著多核的日益普及,越來越多的程序將通過多線程并行化的方式來提升性能。然而,編寫正確的多線程程序一直是一件非常困的事情,volatile關鍵字的使用就是其中一個典型的例子。C/C++中的volatile一般不能用于多線程同步在C/C++中,如果想把一個變量聲明為volatile,就相當于告訴編譯器這個變量是“易變的”,他隨時可能在其他地方被修改,所以編譯器不能對其做任何變化:即每次讀寫該變量時都必須對其內存地址直接進行操作,并且所以對該變量的操作都必須嚴格按照程序中規(guī)定的順序執(zhí)行。舉例來說,編譯器的常常做的一種性能優(yōu)化就是把需頻繁讀取的變量緩存到寄存器中,以提升訪問速度。但如果該變量的值隨時可能在片外被改變的話,那么就有可能出現被緩存的值并不是該變量的最新值情況,從而出現運行錯誤。在這種情況就需要用volatile關鍵字來修飾這個變量,以確保編譯器不會對該變量讀寫操作進行任何緩存優(yōu)化。另一個例子就是內存映射I/O操作。如下代碼所示:Int *p = get_io_address();Int a, b;A = *p;B = *p;P是一個指向硬件I/O端口的指針,該端口的值在每進行一次讀操作后都會變化。這個程序連續(xù)對該端口進行兩次讀取操作已將兩個不同的值分別賦值給a和b。如果不把a和b聲明為volatile的話,編譯器可能會”自作聰明”地認為兩次從p讀取的值都是一樣的,從而把*b=*p優(yōu)化成b = a,最終導致程序出錯。雖然C/C++中volatile關鍵字對這種“易變“的讀寫操作能起到一定的保護,但他卻并不適用于多線程程序中共享變量的同步操作。究其根源,就在于C/C++標準中并沒有volatile賦予原子性和順序性的語義。原子性下面舉個例子說明原子性。i++這看似原子的語句其實有三個操作組成:將該值從內存地址讀取到寄存器中,對寄存器中的值進行加1操作,最后再將新值寫回內存中,正是因為i++并不是原子的,所以如果兩個線程同時進行i++操作的話仍會產生數據競跑,從而導致i的最終值不等于2.在這種情況下,C/C++中的volatile關鍵字根本無法對該操作的原子性提供任何保障。Volatile int  i=0;//線程1I++;//線程2I++;順序性不幸的是,現在C/C++標準中的volatile關鍵字對共享變量操作的順序性也未提供任何保障。以本文中的dekker算法為例:當兩個線程分別執(zhí)行dekker1和dekker2函數時候,改程序通過對flag1/2和turn的讀寫來實現兩個線程對臨界區(qū)中共享變量gCounter的互斥訪問。這個算法的關鍵就在于對flag1/2和turn的讀寫操作是在其寫操作之后進行的,因此它能保證dekker1和dekker2中對gCounterde的操作時互斥的,相當于把gCounter++放到一個臨界區(qū)中去了。Dekker算法如下所示:Volatile int flag1 = 0;Volatile int flag2 = 0;Volatile int turn = 1;Volatile int gCounter = 0;Void dekker1(){       Flag1 = 1;       Turn = 2;While( (flag2 == 1) && ( turn == 2) ){}//進入臨界區(qū)       gCounter++;       flag1 = 0; //離開臨界區(qū)} Void dekker2(){       Flag2 = 1;       Turn = 2;While( (flag1 == 1) && ( turn == 2) ){}//進入臨界區(qū)       gCounter++;       flag2 = 0; //離開臨界區(qū)} 盡管volatile規(guī)定編譯器不能對同一變量的所有操作進行亂序優(yōu)化,但它卻不能阻止編譯器對不同volatile變量間的操作進行亂序優(yōu)化。例如,編譯器可能把dekker1中的flag2讀操作提到flag1和turn寫操作之前,從而導致對臨界區(qū)的互斥訪問失效,最終gCounter++操作就會出現數據競跑現象。事實上,即使編譯器沒有對這個程序做任何優(yōu)化,volatile 關鍵字也不能阻止多核CPU對該程序的亂序優(yōu)化。以常見的x86硬件來說,它可以對不同變量x,y的store x --àload y進行亂序優(yōu)化,把load y操作提到store x操作之前。這樣的話,dekker1中flag2的讀操作還是有可能會被提到flag1和turn的寫操作之前,最終導致錯誤的計算結果。那為什么編譯器和多核CPU會對多線程程序做這樣的亂序優(yōu)化呢?因為從單核的視角來看,flag1 和 flag2,turn的讀寫操作之間沒有任何依賴關系的,使用編譯器/CPU當然可以對他們進行亂序優(yōu)化以隱藏一部分的內存訪問延遲,從而更好的利用CPU里的流水線。換句話說,這樣的優(yōu)化雖從單線程的角度來講沒有錯,但卻違反了設計這個多線程算法時所期望的多線程語義。要是解決這個問題,我們需要解決這個問題,我們需要自己添加內存柵欄以顯式保證順序性,或者干脆去別去實現這樣的算法,轉而使用類似pthread_mutex_lock這樣的加鎖操作來實現互斥訪問。綜合上述,由于現有的C/C++標準中并沒有對volatile添加原子性和順序性的語義,所以絕大部分C/C++程序中使用volatile來進行多線程同步的用法是錯誤的。其實,我們之所以想用volatile變量進行同步,無非是因為鎖,條件變量等方式的開銷太大,所以想有一種輕量級的,高效的同步機制。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 磴口县| 滕州市| 南木林县| 肥东县| 图木舒克市| 梅河口市| 三原县| 长宁县| 德惠市| 禹城市| 文昌市| 拉孜县| 陕西省| 孟州市| 华宁县| 中阳县| 株洲县| 壶关县| 广宁县| 类乌齐县| 进贤县| 望江县| 湘潭市| 泰宁县| 醴陵市| 绍兴县| 南川市| 彭水| 商城县| 德格县| 宁乡县| 天峻县| 凤山市| 裕民县| 宜春市| 台南县| 福海县| 石棉县| 航空| 黄山市| 通山县|