C++霧中風景13:volatile解惑

筆者入職百度時,二面面試官的讓我聊聊C++之中的volatile關鍵詞。volatile在Java和C++之中的差異可謂是天差地別,我只是簡單聊了聊Java之中的volatile,面試官對個人回答並不滿意。後續學習《C++ Prmier》時,對volatile的理解也是雲裏霧裏。入職百度以後,發現身邊的同窗時候對volatile也是誤會頗多。(果真是「面試造核彈,工做擰螺絲」)因此筆者花了一些時間,整理了這篇文章,但願各位C++程序員能完全釐清volatilejava

1.volatile的誤會

volatile這個單詞在英文之中的意思是:易變的,不穩定的的含義。因此顧名思義,一旦變量經過了volatile關鍵詞修飾以後,說明變量是易變的和不穩定的。而C++之中最大的誤會就是認爲volatile關鍵詞與併發編程有關,至於爲什麼會引發這樣的誤會呢?筆者以爲罪魁禍首多是下面的緣由:程序員

Java中的volatile

volatile影響最爲深遠的就是Java之中的功用,筆者第一次接觸這個關鍵詞也是在Java之中。(加上數目龐大的Java程序員~~)Java之中volatile的效果是:面試

  • 確保內存可見性
    讀和寫一個volatile變量具備全局有序性。每一個線程訪問一個volatile變量時都會讀取它在內存之中的當前值,而不是使用一個緩存中的值。這樣可以確保當某線程對volatile變量進行了修改後,後面執行的其餘線程能看到volatile變量的變更。因此在簡單的多線程程序之中,volatile變量能夠起到輕量級的同步做用。可是volatile關鍵字並不保證線程讀寫變量的相對順序,因此適用場景有限。
  • 禁止指令重排序
    指令重排序是JVM 爲了提升程序的運行效率,在不影響單線程程序執行結果的前提下,對各類指令執行的過程進行從新排序和優化,來增長程序處理時的並行度。指令重排序在單線程下可以保證正確,可是在多線程的環境下就很難確保指令重排序的語義正確。

JDK5引入concurrent包中atomic,JDK6將synchronized關鍵字的性能優化後。絕大多數場景,筆者都再也不推薦使用volatile這個關鍵字了。編程

MSVC 微軟的鍋

早期的 MSVC之中 volatile 具備ReleaseAcquire語義,這我想給許多 C++程序猿形成了誤解。後續微軟將這個關鍵字作了一個切換:volatile:ms,用加 ms 的修飾來延續以前的語義。
volatile::ms 的特殊語義緩存

2.volatile 的做用

其實上一節對volatile 的誤用作了討論。接下來筆者來帶你們看看,實際 volatile 關鍵字到底起到怎麼樣的做用。先看以下的代碼:性能優化

int n = 10;                                                                                         
int main() {
    int a = n;
    int b = n;
    return 0;
}

咱們將這段代碼轉換爲彙編代碼,筆者這裏使用的** gcc 版本爲5.4.0
彙編代碼1
接下來,咱們將變量添加上
volatile**關鍵字,看看會出現什麼效果:多線程

int n = 10;                                                                                         
int main() {
    volatile int a = n;
    volatile int b = n;
    return 0;
}

從新編譯這部分代碼轉換爲彙編代碼,咱們來看看:
彙編代碼2
看到這部分彙編代碼,想必各位應該對 volatile關鍵詞的做用應該心中有數了。volatile至關於顯式的要求編譯器禁止對 volatile 變量進行優化,而且要求每一個變量賦值時,須要顯式從寄存器%eax拷貝。volatile 關鍵字在嵌入式編程之中會須要用到,在特定環境下,寄存器的變量可能會發生變化。volatile 因此聲明瞭寄存器部分的數據是『易變的』,須要防止編譯器優化變量,強制載入寄存器。併發

可是若是須要實現相似 Java 之中 volatile 的效果呢?能夠在代碼之中顯式插入內存屏障,讓 CPU 強制刷新寄存器的變量到內存之中。性能

int n = 10;                                                                                         
int main() {
    volatile int a = n;
    asm volatile("" ::: "memory");
    volatile int b = n;
    return 0;
}

如今咱們再來看看編譯生成的彙編代碼:
彙編代碼3
由上述的彙編代碼咱們能夠看到,在添加了內存屏障以後,對變量的賦值操做須要顯式的內存之中取值。實際 Java 在實現 volatile 關鍵字時,也是經過上述語句來實現的。學習

3.小結

volatile 關鍵字自己在 現代的C++和 Java 之中都再也不推薦使用了。在 C++之中有不少對 volatile 的誤用。但願這篇文章可以幫助你們解惑 volatile ,可以正確的進行使用。學有不精,若有謬誤,請多多指教~~~

相關文章
相關標籤/搜索