一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

C語(yǔ)言中的volatile是什么

jf_78858299 ? 來(lái)源:碼農(nóng)的荒島求生 ? 作者:碼農(nóng)的荒島求生 ? 2023-02-17 14:29 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

學(xué)C語(yǔ)言時(shí)有一個(gè)奇怪的關(guān)鍵字volatile,這到底有什么用呢?

volatile與編譯器

首先來(lái)看這樣一段代碼:

int busy = 1;
void wait() { while(busy) { ; }}

編譯一下,注意, 這里使用O2優(yōu)化

圖片

讓我們仔細(xì)看看生成的這段匯編

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

其中L2這一段即為while循環(huán),這段指令是經(jīng)過(guò)編譯器優(yōu)化的,可以看到,決定能否跳出循環(huán)是通過(guò)檢查寄存器eax來(lái)完成的,而沒(méi)有檢查變量busy所在內(nèi)存的真實(shí)內(nèi)容。

注意,對(duì)于這段代碼來(lái)說(shuō)這里的優(yōu)化是正確的,但問(wèn)題是如果還有其它代碼修改了變量busy,那么這里的優(yōu)化會(huì)導(dǎo)致其它代碼對(duì)變量busy的修改根本就不能生效,就像這樣:

int busy = 1;
// 該函數(shù)在A線程中執(zhí)行void wait() { while(busy) { ; }}
// 該函數(shù)在B線程中執(zhí)行void signal() { busy = 0;}

如果wait函數(shù)中while循環(huán)對(duì)應(yīng)的機(jī)器指令僅僅從寄存器中讀取數(shù)據(jù)那么即使B線程的signal函數(shù)修改了busy變量也不能讓wait函數(shù)從循環(huán)中跳出來(lái)。

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

圖片

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

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

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

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

圖片

因此在這里我們需要告訴編譯器:“不要耍小聰明,不要只從寄存器中讀數(shù)據(jù),這個(gè)變量可能在其它地方已經(jīng)被修改了,使用時(shí)從內(nèi)存中獲取最新數(shù)據(jù)”。

現(xiàn)在是時(shí)候簡(jiǎn)單總結(jié)一下了, volatile僅僅阻止編譯器試圖去優(yōu)化對(duì)變量的讀取操作 。

volatile與多線程

一定要注意volatile僅僅確保變量的可見(jiàn)性,但和變量的原子訪問(wèn)沒(méi)有半毛錢(qián)關(guān)系,這是兩個(gè)完全不同的任務(wù) 。

假設(shè)有一個(gè)非常復(fù)雜的結(jié)構(gòu)體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只是確保了當(dāng)該變量被thread1修改后我們能在thread2中讀取到最新值, 但是這解決不了多線程并發(fā)讀寫(xiě)需要原子訪問(wèn)foo的問(wèn)題

確保變量原子性訪問(wèn)一般都采用鎖,當(dāng)使用鎖時(shí),鎖本身就包含了volatile提供能力,即,確保變量的可見(jiàn)性,因此當(dāng)使用鎖時(shí)沒(méi)有必要使用volatile。

volatile與memory order

有的同學(xué)可能會(huì)想如果我想用volatile修飾的變量沒(méi)有那么復(fù)雜,僅僅是一個(gè)int,就像這樣:

volatile int busy = 0;

A線程讀取busy變量,B線程更新busy變量,當(dāng)A檢測(cè)到busy變化后執(zhí)行特定操作,這樣可行嗎?既然通過(guò)volatile修飾后可以確保每次都從內(nèi)存中讀取busy,那么應(yīng)該可以這樣使用吧。

然而,計(jì)算機(jī)在概念上可能相對(duì)簡(jiǎn)單些,但在工程實(shí)踐中是復(fù)雜的。

我們知道由于CPU與內(nèi)存之間的速度差異非常大,CPU與內(nèi)存之間有一層cache,CPU其實(shí)并沒(méi)有直接讀取內(nèi)存,cache的存在會(huì)讓問(wèn)題復(fù)雜起來(lái),限于篇幅與本文主題這里不再展開(kāi)。

為優(yōu)化內(nèi)存讀寫(xiě),CPU可能會(huì)對(duì)內(nèi)存讀寫(xiě)操作進(jìn)行指令重排,reordering,帶來(lái)的后果就是:假設(shè)在線程1中先后執(zhí)行第N行代碼與第N+1行代碼,但在線程2看來(lái)卻是第N+1行代碼先生效,假設(shè)X的初始值為0,Y的初始值為1:

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

當(dāng)線程2檢測(cè)到busy為0后讀取X的值,此時(shí)讀取到的X值可能為0。

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

內(nèi)存屏障是一類(lèi)機(jī)器指令,該指令對(duì)處理器在該屏障指令之前與之后的內(nèi)存操作進(jìn)行了限制,確保不會(huì)出現(xiàn)重排問(wèn)題。

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

可以看到,在多線程環(huán)境下我們幾乎總是不會(huì)使用volatile關(guān)鍵字。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • C語(yǔ)言
    +關(guān)注

    關(guān)注

    180

    文章

    7632

    瀏覽量

    141598
  • 匯編
    +關(guān)注

    關(guān)注

    2

    文章

    214

    瀏覽量

    26558
  • volatile
    +關(guān)注

    關(guān)注

    0

    文章

    46

    瀏覽量

    13345
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    c語(yǔ)言中 volatile _Bool 關(guān)鍵字說(shuō)明

    這個(gè)帖子送給沒(méi)有太多實(shí)踐經(jīng)驗(yàn)的人,如果是大神,請(qǐng)?zhí)^(guò)首先 _Bool 是C99新增的一個(gè),用法和bool一樣, 如下圖: 雖然故意初始化為2,但是下邊變量顯示的值為1;然后為什么我要用
    發(fā)表于 01-06 10:46

    請(qǐng)問(wèn)C語(yǔ)言中volatile有的作用是什么?

    C語(yǔ)言中volatile有什么用作用是什么?看到一個(gè)數(shù)組,別人這樣寫(xiě):volatile unsigned short adc_dma_buf[40];u32 ADCValue_ActC
    發(fā)表于 10-23 22:16

    STM32CubeMX的相關(guān)資料分享

    鏈接5中的代碼)1.C語(yǔ)言中volatile關(guān)鍵字的學(xué)習(xí)2.【STM32】HAL庫(kù) STM32CubeMX教程十一—DMA (串口DMA發(fā)送接收)3.C
    發(fā)表于 01-14 07:10

    c語(yǔ)言中volatile關(guān)鍵字作用

    一個(gè)定義為volatile 的變量是說(shuō)這變量可能會(huì)被意想不到地改變,這樣,編譯器就不會(huì)去假設(shè)這個(gè)變量的值了。精確地說(shuō)就是,優(yōu)化器在用到這個(gè)變量時(shí)必須每次都小心地重新讀
    發(fā)表于 06-22 17:04 ?46次下載

    C語(yǔ)言中volatile關(guān)鍵字

    volatile關(guān)鍵字是一種類(lèi)型修飾符,用它聲明的類(lèi)型變量表示可以被某些編譯器未知的因素更改。
    發(fā)表于 05-27 09:32 ?3333次閱讀

    C語(yǔ)言中指針的介紹非常詳細(xì)

    C語(yǔ)言中指針的介紹非常詳細(xì) C語(yǔ)言中指針的介紹非常詳細(xì)
    發(fā)表于 12-25 10:39 ?57次下載

    C語(yǔ)言和匯編語(yǔ)言混合編程方法和C語(yǔ)言中斷處理方法

    C語(yǔ)言和匯編語(yǔ)言混合編程方法和C語(yǔ)言中斷處理方法,new
    發(fā)表于 01-06 14:36 ?36次下載

    C語(yǔ)言中的關(guān)鍵字

    C語(yǔ)言中的入門(mén)教程
    發(fā)表于 10-14 16:24 ?3次下載

    C語(yǔ)言中volatile關(guān)鍵字的使用

    volatile 的意思是易變的、可變的,作用是限制編譯器優(yōu)化某些變量。首先看一段C51程序: Keil在優(yōu)化級(jí)別是為8時(shí)得到如下匯編代碼(部分未列出):可以看到,變量d的值賦給x,y,z時(shí),只有x
    的頭像 發(fā)表于 03-17 11:55 ?1.3w次閱讀
    <b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言中</b><b class='flag-5'>volatile</b>關(guān)鍵字的使用

    C語(yǔ)言類(lèi)型修飾符Volatile的使用說(shuō)明

    C語(yǔ)言是我們經(jīng)常需要用到的語(yǔ)言,C語(yǔ)言中的類(lèi)型修飾符Volatile大家知道怎么使用嗎?
    的頭像 發(fā)表于 09-19 10:54 ?3765次閱讀

    總結(jié)那么幾個(gè)C語(yǔ)言中的“坑”

    總結(jié)幾個(gè)C語(yǔ)言中的“坑”
    的頭像 發(fā)表于 01-16 10:52 ?2908次閱讀

    【嵌入式】C語(yǔ)言中volatile關(guān)鍵字

    volatile06. 附錄01. volatile概述volatileC語(yǔ)言中的一個(gè)關(guān)鍵字。將變量定義為
    發(fā)表于 10-21 10:21 ?6次下載
    【嵌入式】<b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言中</b><b class='flag-5'>volatile</b>關(guān)鍵字

    HAL庫(kù)STM32串口DMA不定長(zhǎng)收發(fā),空閑中斷

    參考了鏈接5中的代碼)1.C語(yǔ)言中volatile關(guān)鍵字的學(xué)習(xí)2.【STM32】HAL庫(kù) STM32CubeMX教程十一—DMA (串口DMA發(fā)送接收)3.C
    發(fā)表于 01-14 10:40 ?19次下載
    HAL庫(kù)STM32串口DMA不定長(zhǎng)收發(fā),空閑中斷

    c#語(yǔ)言中怎么使用HTTP代理

    c#語(yǔ)言中怎么使用HTTP代理。
    的頭像 發(fā)表于 09-01 14:46 ?2372次閱讀

    C語(yǔ)言中的socket編程基礎(chǔ)

    Socket編程簡(jiǎn)介 Socket是一種通信機(jī)制,允許程序之間進(jìn)行通信。在C語(yǔ)言中,socket編程是網(wǎng)絡(luò)編程的基礎(chǔ)。通過(guò)使用socket,程序可以發(fā)送和接收數(shù)據(jù),實(shí)現(xiàn)不同計(jì)算機(jī)之間的通信
    的頭像 發(fā)表于 11-01 16:51 ?1214次閱讀