c c volatile關鍵字詳解

2021-10-08 18:26:24 字數 2310 閱讀 6474

最近看boost併發的相關文件,發現這個關鍵字我沒怎麼用過,慚愧慚愧,特意學習。

volatile 關鍵字在c/c++中與const同級用來修飾變數,通常建立語言級別的memory barrier,用他宣告的變數可以被某些編譯器未知的因素修改,如作業系統,硬體,其他執行緒等。遇到這個關鍵字宣告的變數,編譯器就不在對其進行優化,當使用violatile宣告變數的時候,系統總是從他所在的記憶體空間直接讀取資料,而一般情況下,編譯器會對**進行優化。在連續讀取某個記憶體位址儲存的變數時,如果位址沒有變化,且在該單執行緒中沒有其他語句改變這個變數中的值,則編譯器不會直接去讀記憶體中變數的值,而是從暫存器中直接讀取原先邊變數的值,所以當有作業系統,硬體,或者其他執行緒(大多數其他執行緒爭強共享變數時會發生)對改變量進行修改時,就會出現讀錯誤,讀出的資料並不是變數現在真正儲存的值,從而導致錯誤的發生。

綜上,volatile可以保證每次對變數的訪問都是從記憶體位址直接讀取,而不是被編譯器優化後可能導致的讀錯誤。

bool extern_tag =

false

;// 定義乙個全域性變數,會有多個執行緒對此變數進行訪問

void

thread1

(void

*arg)

}void

thread2

(void

*arg)

intmain

(int argc,

char

**ar**)

上述**,你會發現,在thread2執行之後,也就是thread1已經執行了1s之後,thread2把全域性變數extern_tag置為true,也就是將thread1中的while迴圈的判斷條件置為false,理論上,此時,thread1就應該退出迴圈,但是我們會發現,在tag被置位後,thread1的while迴圈依舊在執行,直至主函式結束,執行緒被**,原因就是編譯器對tag的讀取進行了優化,在編譯器檢查到thread1並沒有對tag進行修改操作時,主動對tag的讀取進行了優化,不在從記憶體位址去讀取tag的值,而是用上一次的值替換這次的讀取,而實際上tag的值已經被另乙個執行緒thread2給修改,來藉此來關閉thread1,但是這個操作並沒有被感知到,讀取的tag依舊是原值,thread1並沒有被關閉,導致錯誤。

相對於作業系統和硬體對變數的不可見修改導致讀取錯誤,更多的實際情況是我們在併發程式設計中,多個執行緒對共享變數的訪問導致的讀錯誤更加常見,volatile關鍵字,以下幾個情況需要加volatile來保證變數每次都從記憶體中讀取。

中斷服務程式中修改的供其他程式檢測的控制變數,即乙個執行緒或程序修改乙個標記變數來關閉另乙個執行緒或程序,這個標記變數需要加修飾。

多工環境下,各任務間共享的標誌。

儲存器對映的硬體暫存器,通常也要加volatile修飾,也屬於高訪問度的全域性變數,要保證每次讀取都是從位址實際讀取出來的。

與const修飾詞同級,volatile也有頂層volatile和底層volatile的說法。

int

*volatile a;

// 頂層volatile,代表指標a變數是乙個volatile變數,每次對該指標變數的訪問,都是直接從該指標的記憶體變數位址直接訪問,不會被編譯器優化

volatile

int*a;

// 底層volatile,代表指標a指向的物件是乙個volatile變數,所有對該指標指向的物件的操作,都是直接從該物件的記憶體位址中直接操作,沒有編譯器優化

可以把乙個非volatile int賦給volatile int,但是不能把非volatile物件賦給乙個volatile物件。

除了基本型別外,對使用者定義型別也可以用volatile型別進行修飾。

c++中乙個有volatile識別符號的類只能訪問它介面的子集,乙個由類的實現者控制的子集。使用者只能用const_cast來獲得對型別介面的完全訪問。此外,volatile向const一樣會從類傳遞到它的成員。

我最常用的使用volatile關鍵字的情況還是在我們最頭疼的領域,併發程式設計,比如要開發一款對底層資料操作並管理的sdk,其中需要啟動多個執行緒來對乙個共享的佇列進行併發操作,對於這些執行緒的控制可以選擇使用共享變數來對他們進行控制標記,根據該變數的不同的值從而實現對其他執行緒的控制,這時對這些控制變數標記volatile就是乙個必要的選擇,因為這些變數會時常被其他執行緒改變,如果不加以控制,在乙個執行緒內,由於編譯器的優化,會將改變量的值儲存在cpu暫存器中,在下一次讀取時,編譯器發現在該執行緒內並沒有對該執行緒的操作和改值,所以直接從暫存器而不是從記憶體中讀取該變數,而此時可能有其他執行緒已經改變了此變數的值來控制其他執行緒,但這些執行緒並不會讀出變數的改變,因為他們是從暫存器而不是記憶體讀取該值,造成很嚴重的併發錯誤,這裡要十分注意。

具體例子在簡介中已經介紹,可看上文。

C C volatile關鍵字用法總結

volatile關鍵字是一種型別修飾符,用它宣告的型別變數表示可以被某些編譯器未知的因素更改,比如 作業系統 硬體或者其它執行緒等。遇到這個關鍵字宣告的變數,編譯器對訪問該變數的 就不再進行 優化,從而可以提供對特殊位址的穩定訪問。使用該關鍵字的例子如下 int volatile nvint 當要求...

關鍵字const詳解

關鍵字const 1 用關鍵字const定義變數指所定義的常量。即恆定的變數,即不可改變的變數。例 const int i 10 此時 i 就是乙個整型的常量,其值不能改變。在c 中如果用const定義乙個物件,那麼物件中的成員變數就是常數,不能改變 只能為初始化的值 如果用const定義類中的成員...

關鍵字volatile詳解

與關鍵字const一樣,關鍵字volatile也是乙個型別修飾符 type specifier 關於volatile的作用 例如程式清單 volatile int m 10 int k,n m k i volatile將告訴編譯器,整形變數m是隨時發生變化的,每次使用m的時候,都要求從m的位址中找出...