C/C++的volatile關(guān)鍵字應(yīng)用示例

首先必須強調(diào)volatile無法用來保證線程安全子库。

volatile的功能是阻止編譯器優(yōu)化揍异,從而直接從內(nèi)存中讀寫變量的值现拒。由于操作系統(tǒng)訪問寄存器的速度遠大于訪問內(nèi)存的速度(兩者之間還有各級cache)蔓腐,所以編譯程序時可能會進行優(yōu)化叮阅,比如這樣的語句

int flag = 1;
while (flag) {}

由于多次對flag進行判斷刁品,所以編譯器可能優(yōu)化為把flag變量的值從內(nèi)存中拷貝到寄存器中,之后每次都從寄存器中讀取浩姥。從代碼的層面看起來沒有問題挑随,但是比如信號處理函數(shù)改變了flag的值,更新的flag不會立刻(甚至不會)反映到寄存器中勒叠,因此讀取的flag的值還是舊的值兜挨。
給出示例代碼

// test.cc
#include <stdio.h>
#include <signal.h>

static int g_iRun = 1;

void sigint_handler(int) { g_iRun = 0; }

int main() {
    if (signal(SIGINT, sigint_handler) == SIG_ERR)
        perror("signal SIGINT");
    while (g_iRun) {}
    printf("sigint caught!\n");
    return 0;
}
$ g++ test.cc 
$ ./a.out 
^Csigint caught!
$ g++ test.cc -O
$ ./a.out 
^C^C^C^C^\Quit (core dumped)

可以看到僅僅加了-O選項,最低層次的優(yōu)化下信號處理器都不會對Ctrl+C做出反應(yīng)眯分。
為了防止程序直接從寄存器中讀取變量的值拌汇,需要用volatile來修飾g_iRun變量
static volatile int g_iRun = 1;
修飾之后,即使用-O3選項進行優(yōu)化弊决,仍然可以捕捉信號

$ g++ test.cc -O3
$ ./a.out 
^Csigint caught!

另一個典型應(yīng)用就是APUE上圖7-13的示例程序噪舀,C程序使用setjmplongjmp回滾函數(shù)棧幀時,自動變量的值是否回滾是不確定的飘诗。

// test.cc
#include <stdio.h>
#include <setjmp.h>

static jmp_buf jmpbuffer;

void func() { longjmp(jmpbuffer, 1); }

int main() {
    int x = 1;
    if (setjmp(jmpbuffer) == 0) {  
        x = 2;
        func();
    } else {  // 從longjmp中返回
        printf("%d\n", x);
    }
    return 0;
}
$ g++ test.cc 
$ ./a.out 
2
$ g++ test.cc -O
$ ./a.out 
1

和處理信號的示例一樣与倡,用了優(yōu)化選項-O編譯后,自動變量x的值在longjmp后從2變成了1昆稿。如果用volatile修飾自動變量x纺座,那么longjmp之后x的值保證為2。

最后說說為什么無法保證線程安全溉潭。
線程安全指在多線程環(huán)境下净响,無論多線程如何交替執(zhí)行少欺,最后的結(jié)果都是預(yù)期值。比如N個線程對變量x執(zhí)行x = x + 1操作馋贤,最后x的值增加了N狈茉。
舉個經(jīng)典例子,2個線程對volatile變量x(初值為0)執(zhí)行自增操作掸掸,既可能是這樣的執(zhí)行順序

  1. 線程A從內(nèi)存中讀取x的值(A.x=0)氯庆;
  2. 線程B從內(nèi)存中讀取x的值(B.x=0);
  3. 線程A寫入值(A.x+1=1)到x的內(nèi)存中(x此時為1)扰付;
  4. 線程B寫入值(B.x+1=1)到x的內(nèi)存中(x此時為1)堤撵;

也有可能時這樣的執(zhí)行順序

  1. 線程A從內(nèi)存中讀取x的值(A.x=0);
  2. 線程A寫入值(A.x+1=1)到x的內(nèi)存中(x此時為1)羽莺;
  3. 線程B從內(nèi)存中讀取x的值(B.x=1)实昨;
  4. 線程B寫入值(B.x+1=2)到x的內(nèi)存中(x此時為2);

兩種不同的執(zhí)行順序?qū)е铝瞬煌慕Y(jié)果盐固,但是兩個線程都是直接從內(nèi)存中讀寫變量x荒给。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市刁卜,隨后出現(xiàn)的幾起案子志电,更是在濱河造成了極大的恐慌,老刑警劉巖蛔趴,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挑辆,死亡現(xiàn)場離奇詭異,居然都是意外死亡孝情,警方通過查閱死者的電腦和手機鱼蝉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來箫荡,“玉大人魁亦,你說我怎么就攤上這事「岬玻” “怎么了洁奈?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長婉弹。 經(jīng)常有香客問我睬魂,道長终吼,這世上最難降的妖魔是什么镀赌? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮际跪,結(jié)果婚禮上商佛,老公的妹妹穿的比我還像新娘喉钢。我一直安慰自己,他們只是感情好良姆,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布肠虽。 她就那樣靜靜地躺著,像睡著了一般玛追。 火紅的嫁衣襯著肌膚如雪税课。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天痊剖,我揣著相機與錄音韩玩,去河邊找鬼。 笑死陆馁,一個胖子當著我的面吹牛找颓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播叮贩,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼击狮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了益老?” 一聲冷哼從身側(cè)響起彪蓬,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎捺萌,沒想到半個月后寞焙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡互婿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年捣郊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片慈参。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡呛牲,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出驮配,到底是詐尸還是另有隱情娘扩,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布壮锻,位于F島的核電站琐旁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏猜绣。R本人自食惡果不足惜灰殴,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望掰邢。 院中可真熱鬧牺陶,春花似錦伟阔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至狮鸭,卻和暖如春合搅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背歧蕉。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工历筝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人廊谓。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓梳猪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蒸痹。 傳聞我的和親對象是個殘疾皇子春弥,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359