成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

C語言中的volatile到底有什么用?

開發 前端
一定要注意volatile僅僅確保變量的可見性,但和變量的原子訪問沒有半毛錢關系,這是兩個完全不同的任務。

?大家好,我是小風哥。

學C語言時有一個奇怪的關鍵字volatile,這到底有什么用呢?

volatile與編譯器

首先來看這樣一段代碼:

int busy = 1;


void wait() {
while(busy) {
;
}
}

編譯一下,注意,這里使用O2優化:

圖片

讓我們仔細看看生成的這段匯編:

wait:
mov eax, DWORD PTR busy[rip]
.L2:
test eax, eax
jne .L2
ret
busy:
.long 1

其中L2這一段即為while循環,這段指令是經過編譯器優化的,可以看到,決定能否跳出循環是通過檢查寄存器eax來完成的,而沒有檢查變量busy所在內存的真實內容。

注意,對于這段代碼來說這里的優化是正確的,但問題是如果還有其它代碼修改了變量busy,那么這里的優化會導致其它代碼對變量busy的修改根本就不能生效,就像這樣:

int busy = 1;


// 該函數在A線程中執行
void wait() {
while(busy) {
;
}
}


// 該函數在B線程中執行
void signal() {
busy = 0;
}

如果wait函數中while循環對應的機器指令僅僅從寄存器中讀取數據那么即使B線程的signal函數修改了busy變量也不能讓wait函數從循環中跳出來。

如果你對busy變量使用volatile修飾,生成的指令就變成這樣了:

圖片

wait:
.L2:
mov eax, DWORD PTR busy[rip]
test eax, eax
jne .L2
ret
busy:
.long 1

注意看此時L2這一段,每次都從busy變量所在的內存中讀取數據并存放在eax,然后再去判斷,這樣就能確保每次都能讀取到busy變量的最新值。

實際上你可以把寄存器eax當做busy所在內存的cache,當cache(寄存器)和內存中的數據一致時不會有任何問題,但當cache與內存中的數據不一致時(也就是內存已被更新但cache保存的還是舊數據),程序的運行往往出乎預料。

除了多線程的例子,還有一類就是signal handler以及硬件修改該變量(用C語言與硬件交互式時經常遇到),如果編譯器生成文章開頭那樣的指令那么等待線程將檢測不到signal handler或者硬件對變量的修改。

圖片

因此在這里我們需要告訴編譯器:“不要耍小聰明,不要只從寄存器中讀數據,這個變量可能在其它地方已經被修改了,使用時從內存中獲取最新數據”。現在是時候簡單總結一下了,volatile僅僅阻止編譯器試圖去優化對變量的讀取操作。

volatile與多線程

一定要注意volatile僅僅確保變量的可見性,但和變量的原子訪問沒有半毛錢關系,這是兩個完全不同的任務。假設有一個非常復雜的結構體struct foo:

struct data {
int a;
int b;
int c;
...
};


volatile struct data foo;


void thread1() {
foo.a = 1;
foo.b = 2;
foo.c = 3;
...
}


void thread2() {
int a = foo.a;
int b = foo.b;
int c = foo.c;
...
}

你僅僅用volatile去修飾變量foo只是確保了當該變量被thread1修改后我們能在thread2中讀取到最新值,但是這解決不了多線程并發讀寫需要原子訪問foo的問題。

確保變量原子性訪問一般都采用鎖,當使用鎖時,鎖本身就包含了volatile提供能力,即,確保變量的可見性,因此當使用鎖時沒有必要使用volatile。

volatile與memory order

有的同學可能會想如果我想用volatile修飾的變量沒有那么復雜,僅僅是一個int,就像這樣:

volatile int busy = 0;

A線程讀取busy變量,B線程更新busy變量,當A檢測到busy變化后執行特定操作,這樣可行嗎?既然通過volatile修飾后可以確保每次都從內存中讀取busy,那么應該可以這樣使用吧。

然而,計算機在概念上可能相對簡單些,但在工程實踐中是復雜的。

我們知道由于CPU與內存之間的速度差異非常大,CPU與內存之間有一層cache,CPU其實并沒有直接讀取內存,cache的存在會讓問題復雜起來,限于篇幅與本文主題這里不再展開。

為優化內存讀寫,CPU可能會對內存讀寫操作進行指令重排,reordering,帶來的后果就是:假設在線程1中先后執行第N行代碼與第N+1行代碼,但在線程2看來卻是第N+1行代碼先生效,假設X的初始值為0,Y的初始值為1:

線程1           線程2
X = 10 if (!busy)
busy = 0; Y = X;

當線程2檢測到busy為0后讀取X的值,此時讀取到的X值可能為0。

為解決這一問題,我們需要的不是volatile,volatile解決不了reordering問題,我們需要的是內存屏障,memory barrier。

內存屏障是一類機器指令,該指令對處理器在該屏障指令之前與之后的內存操作進行了限制,確保不會出現重排問題。

而內存屏障帶來的效果依然能夠涵蓋volatile提供的功能,因此也不需要volatile。

可以看到,在多線程環境下我們幾乎總是不會使用volatile關鍵字。?

責任編輯:武曉燕 來源: 碼農的荒島求生
相關推薦

2020-10-20 09:57:04

量子計算人工智能技術

2023-09-07 23:06:07

2022-05-05 07:38:32

volatilJava并發

2024-02-28 16:18:41

2020-04-30 09:41:04

數據中臺CIO觀點

2019-05-16 10:30:49

JavaTCP協議

2019-10-14 10:29:42

Java消息隊列

2022-05-24 12:57:49

函數代碼Java

2021-05-11 10:44:51

飛行模式通信設備通信干擾

2022-05-17 08:41:41

協程I/O模式

2024-10-15 09:48:56

2021-04-28 09:55:52

JavaLock接口并發編程

2021-12-28 20:05:19

數字交通信息

2018-06-26 14:29:44

LinuxUnix不同

2021-02-02 07:37:39

NextTickvueDOM

2020-08-05 07:00:00

SSD硬盤存儲

2022-09-14 09:45:15

指標標簽

2012-07-25 15:45:28

ERPSCM

2024-02-26 07:36:09

lockJava語言

2019-10-14 10:09:33

Wi-Fi 6Wi-Fi無線網絡
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产一区视频在线 | 国产福利网站 | 青青草一区二区 | caoporn地址 | a在线观看 | 国产一区三区视频 | 国产精品一区三区 | 亚洲va在线va天堂va狼色在线 | 国产69久久精品成人看动漫 | 久久99精品国产 | 国产目拍亚洲精品99久久精品 | 久草青青草 | 青青久视频 | 成人高清视频在线观看 | 精品国产一区二区三区在线观看 | 日本不卡一区二区 | 日韩中文在线观看 | www.黄网| www.亚洲一区二区 | 国产高清在线精品 | 亚洲精品欧美 | 国产毛片久久久久久久久春天 | 天天操 夜夜操 | 97视频精品 | 日本欧美在线观看视频 | 最新超碰| 亚洲综合小视频 | 成人免费毛片在线观看 | 久久国产精品偷 | 色综合色综合网色综合 | 日本亚洲欧美 | 日韩精品视频在线观看一区二区三区 | avmans最新导航地址 | 日韩精品在线免费观看视频 | 久久国产精品视频 | 色站综合| 日韩成人免费中文字幕 | 国产精品久久久久久久久久久久 | 日本人做爰大片免费观看一老师 | 精品成人在线视频 | 国产精品国产亚洲精品看不卡15 |