Volatile深入理解

来源:互联网 发布:淘宝卖家服务热线 编辑:程序博客网 时间:2024/05/02 02:01

转自:http://bbs.eeworld.com.cn/thread-299390-1-1.html

    就象大家更熟悉的const,auto,register等关键字一样,volatile是一个类型修饰符。它是被设计用来修饰被不同线程访问和修改的变量。如果没有volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么编译器失去大量优化的机会。Volatile 是易变的、不稳定的意思。用它修饰的变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。


下面举个例子说明一下问题


int m=10;


int n=m;


当编译完之后,它不会改变,所以不会每次都从内存中读取m的值。


volatile int m=10;


int n=m;


    因为volatile是不稳定的,所以它的值可能发生变化。每次使用它的时候必须从内存中取出i的值,因而编译器生成的汇编代码会重新从m 的地址处读取数据放在n中。所以 必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。


在嵌入式编程中,这种情况经常出现,例如volatile int flag=1;//前面加了volatile则flag的值就不会一直是“1”了。


这样看来,如果m是一个寄存器变量或者表示一个端口数据或者是多个线程的共享数据,就容易出错,所以说volatile可以保证对特殊地址的稳定访问。但是注意:在VC++6.0 中,一般Debug 模式没有进行代码优化,所以这个关键字的作用有可能看不出来。你可以同时生成Debug 版和Release 版的程序做个测试。


     下面三种是典型的volatile关键字的运用场合:


(1)并行设备的硬件寄存器(如:状态寄存器)


(2)一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)  


(3) 多线程应用中被几个任务共享的变量  


(4)下面是来此百度百科的一些解释:本人经过小小的修改,方便理解,在此也贴出来:


从编译器的优化方面理解: 


     在本次线程内, 当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中;以后,再取变量值时,就直接从寄存器中取值;当变量值在本线程里改变时,会同时把变量的新值copy到该寄存器中,以便保持一致。这就是所谓的“不稳定”,其实还不如说是“变相的一致性”更好!这也是使用volatile关键字的优势。


    当变量在因别的线程等而改变了值,该寄存器的值不会相应改变,从而造成应用程序读取的值和实际的变量值不一致,当该寄存器在因别的线程等而改变了值,原变量的值不会改变,从而造成应用程序读取的值和实际的变量值不一致,从而会出现错误。这就是没有使用volatile关键字的缺点。   


举一个不太准确的例子:发薪资时,会计每次都把员工叫来登记他们的银行卡号;一次会计为了省事,没有即时登记,用了以前登记的银行卡号;刚好一个员工的银行卡丢了,已挂失该银行卡号;从而造成该员工领不到工资   


员工 -- 原始变量地址 


银行卡号 -- 原始变量在寄存器的备份     


补充: volatile应该解释为“直接存取原始内存地址”比较合适,“易变的”这种解释简直有点误导人; “易变”是因为外在因素引起的,象多线程,中断等,并不是因为用volatile修饰了的变量就是“易变”了,假如没有外因,即使用volatile定义,它也不会变化;  


而用volatile定义之后,其实这个变量就不会因外因而变化了,可以放心使用了;大家看看前面那种解释(易变的)是不是在误导人 。


有volatie修饰的变量,每次操作时遵循下面动作:


从内存取值 ---> 放入寄存器 ---> 操作 --->写回内存


没有volatie修饰的变量,操作可能遵循(可能不是所有情况都如此):


从内存取值 ---> 放入寄存器 ---> 第一次操作 ---> 第二次操作(此时仍操作寄存器中的值) …… --->第N次操作 --->写回内存


volatile 跟以前的 register 相反. register 告诉编译器尽量将变量放到寄存器中使用, 而volatile 强制将更改后的值写回内存(无论是cache还是内存). 如果不写回内存, 对于一些全局共享的变量, 可能导致不一致问题.


volatile在优化中的微妙作用。 
1.阻止编译器优化
volatile隐含地告诉编译器特殊寄存器可能会改变内容,即使没有任何显式地代码去改变它的内容。


2.无意中降低了效率


     总结:使用volatile关键字就是为了是编程方便,让编译器少出现bug,尤其是在硬件和底层编程的时候,经常会用到这个关键字,如果处理的不好,会出现意想不到的结果,从而也会导致程序出错。所以只有真正理解了volatile才会在使用时得心应手。



相当详细啊。


 


我用IAR编译while()的时候就出现过几次错误,就是因为变量没有定义成volatile造成的。 比如while(a < 5)语句,明明看到变量a已经大于5了,循环仍在执行。原因就是定义变量a的时候,没有加volatile说明,IAR编译while(a<5)语句的时候,首先把变量a的值放到一个寄存器中,然后根据寄存器的值判断是否结束循环。虽然,变量a的值已经改变了,但是寄存器的值没有更新,所以循环无法结束。。。。。


特别是一些循环的时候不加就悲剧!

原创粉丝点击