就是每次都要到內存上讀取數據,不能直接用寄存器的數據(第一次直接取自內存而後放在寄存器,第二次編譯器就會優化成使用寄存器的值)html
並不解決多線程的事web
https://liam.page/2018/01/18/volatile-in-C-and-Cpp/安全
被 volatile
修飾的變量,在對其進行讀寫操做時,會引起一些可觀測的反作用。而這些可觀測的反作用,是由程序以外的因素決定的。多線程
所以咱們講,在 C/C++ 中,對 volatile
對象的訪問,有編譯器優化上的反作用:併發
volatile
對象的訪問以前。這裏說起的「不容許被優化」表示對 volatile
變量的訪問,編譯器不能作任何假設和推理,都必須循序漸進地與「內存」進行交互。所以,上述例中「複用寄存器中的值」就是不容許的。app
volatile
不能解決多線程中的問題。volatile
只在三種場合下是合適的。
setjmp
和 longjmp
)相關的場合。https://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777432.htmlide
1. 爲何用volatile?測試
C/C++ 中的 volatile 關鍵字和 const 對應,用來修飾變量,一般用於創建語言級別的 memory barrier。這是 BS 在 "The C++ Programming Language" 對 volatile 修飾詞的說明:優化
A volatile specifier is a hint to a compiler that an object may change its value in ways not specified by the language so that aggressive optimizations must be avoided.操作系統
volatile 關鍵字是一種類型修飾符,用它聲明的類型變量表示能夠被某些編譯器未知的因素更改,好比:操做系統、硬件或者其它線程等。遇到這個關鍵字聲明的變量,編譯器對訪問該變量的代碼就再也不進行優化,從而能夠提供對特殊地址的穩定訪問。聲明時語法:int volatile vInt; 當要求使用 volatile 聲明的變量的值的時候,系統老是從新從它所在的內存讀取數據,即便它前面的指令剛剛從該處讀取過數據。並且讀取的數據馬上被保存。例如:
1 volatile int i=10; 2 int a = i; 3 ... 4 // 其餘代碼,並未明確告訴編譯器,對 i 進行過操做 5 int b = i;
volatile 指出 i 是隨時可能發生變化的,每次使用它的時候必須從 i的地址中讀取,於是編譯器生成的彙編代碼會從新從i的地址讀取數據放在 b 中。而優化作法是,因爲編譯器發現兩次從 i讀數據的代碼之間的代碼沒有對 i 進行過操做,它會自動把上次讀的數據放在 b 中。而不是從新從 i 裏面讀。這樣以來,若是 i是一個寄存器變量或者表示一個端口數據就容易出錯,因此說 volatile 能夠保證對特殊地址的穩定訪問。注意,在 VC 6 中,通常調試模式沒有進行代碼優化,因此這個關鍵字的做用看不出來。下面經過插入彙編代碼,測試有無 volatile 關鍵字,對程序最終代碼的影響:
輸入下面的代碼:
01 #include <stdio.h> 02 03 void main() 04 { 05 int i = 10; 06 int a = i; 07 08 printf("i = %d", a); 09 10 // 下面彙編語句的做用就是改變內存中 i 的值 11 // 可是又不讓編譯器知道 12 __asm { 13 mov dword ptr [ebp-4], 20h 14 } 15 16 int b = i; 17 printf("i = %d", b); 18
而後,在 Debug 版本模式運行程序,輸出結果以下:
i = 10
i = 32
而後,在 Release 版本模式運行程序,輸出結果以下:
i = 10
i = 10
輸出的結果明顯代表,Release 模式下,編譯器對代碼進行了優化,第二次沒有輸出正確的 i 值。下面,咱們把 i 的聲明加上 volatile 關鍵字,看看有什麼變化:
01 #include <stdio.h> 02 03 void main() 04 { 05 volatile int i = 10; 06 int a = i; 07 08 printf("i = %d", a); 09 __asm { 10 mov dword ptr [ebp-4], 20h 11 } 12 13 int b = i; 14 printf("i = %d", b); 15
分別在 Debug 和 Release 版本運行程序,輸出都是:
i = 10
i = 32
這說明這個 volatile 關鍵字發揮了它的做用。其實不僅是「內嵌彙編操縱棧」這種方式屬於編譯沒法識別的變量改變,另外更多的多是多線程併發訪問共享變量時,一個線程改變了變量的值,怎樣讓改變後的值對其它線程 visible。通常說來,volatile用在以下的幾個地方:
1) 中斷服務程序中修改的供其它程序檢測的變量須要加volatile;
2) 多任務環境下各任務間共享的標誌應該加volatile;
3) 存儲器映射的硬件寄存器一般也要加volatile說明,由於每次對它的讀寫均可能由不一樣意義;
2.volatile 指針
和 const 修飾詞相似,const 有常量指針和指針常量的說法,volatile 也有相應的概念:
修飾由指針指向的對象、數據是 const 或 volatile 的:
1 const char* cpch; 2 volatile char* vpch;
注意:對於 VC,這個特性實如今 VC 8 以後纔是安全的。
指針自身的值——一個表明地址的整數變量,是 const 或 volatile 的:
1 char* const pchc; 2 char* volatile pchv;
注意:(1) 能夠把一個非volatile int賦給volatile int,可是不能把非volatile對象賦給一個volatile對象。
(2) 除了基本類型外,對用戶定義類型也能夠用volatile類型進行修飾。
(3) C++中一個有volatile標識符的類只能訪問它接口的子集,一個由類的實現者控制的子集。用戶只能用const_cast來得到對類型接口的徹底訪問。此外,volatile向const同樣會從類傳遞到它的成員。
3. 多線程下的volatile
有些變量是用volatile關鍵字聲明的。當兩個線程都要用到某一個變量且該變量的值會被改變時,應該用volatile聲明,該關鍵字的做用是防止優化編譯器把變量從內存裝入CPU寄存器中。若是變量被裝入寄存器,那麼兩個線程有可能一個使用內存中的變量,一個使用寄存器中的變量,這會形成程序的錯誤執行。volatile的意思是讓編譯器每次操做該變量時必定要從內存中真正取出,而不是使用已經存在寄存器中的值,以下:
volatile BOOL bStop = FALSE; (1) 在一個線程中: while( !bStop ) { ... } bStop = FALSE; return; (2) 在另一個線程中,要終止上面的線程循環: bStop = TRUE; while( bStop ); //等待上面的線程終止,若是bStop不使用volatile申明,那麼這個循環將是一個死循環,由於bStop已經讀取到了寄存器中,寄存器中bStop的值永遠不會變成FALSE,加上volatile,程序在執行時,每次均從內存中讀出bStop的值,就不會死循環了。 這個關鍵字是用來設定某個對象的存儲位置在內存中,而不是寄存器中。由於通常的對象編譯器可能會將其的拷貝放在寄存器中用以加快指令的執行速度,例以下段代碼中: ... int nMyCounter = 0; for(; nMyCounter<100;nMyCounter++) { ... } ... 在此段代碼中,nMyCounter的拷貝可能存放到某個寄存器中(循環中,對nMyCounter的測試及操做老是對此寄存器中的值進行),可是另外又有段代碼執行了這樣的操做:nMyCounter -= 1;這個操做中,對nMyCounter的改變是對內存中的nMyCounter進行操做,因而出現了這樣一個現象:nMyCounter的改變不一樣步。