为什么使用volatile? 你用过volatile吗?
c/c++中的volatile关键字和const对应,用来修饰变量,通常用于建立语言级别的memory barrier。volatile关键字是一种类型修饰符,没有用它声明的类型变量表示可以被某些编译器未知的因素更改比如操作系统,硬件或者其他线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问,声明时语法 volatile int v ,当使用volatile声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据,而且读取的数据立即被保存了。
volatile int i = 10;int a = i;....int b = i;
volatile指出i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在b中,而假如编译器优化的做法是,由于编译器发现两次从i读取数据的代码之间没有对i进行过操作,它会自动把上次读到的数据放在b中,而不是重新从i里面读这样以来如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问。
#include int main() { int i = 10; int a = i; printf("i = %d", a); // 下面汇编语句的作用就是改变内存中 i 的值 // 但是又不让编译器知道 __asm{ mov dword ptr [ebp-4], 20h } int b = i; printf("i = %d", b); getchar();return 0;}
在debug版本模式运行程序输出结果如下:
i = 10
i = 32
在release版本模式运行程序输出结果如下:
i = 10
i = 10
从输出结果明显看出,release模式下编译器对代码进行了优化,第二次没有输出正确的i值
下面我们把i的声明加上volatile关键字看看输出什么?
#include int main() { volatile int i = 10; int a = i; printf("i = %d", a); __asm { mov dword ptr [ebp-4], 20h } int b = i; printf("i = %d", b); getchar();return 0;}
分别在debug和release模式下运行输出都是:
i = 10
i = 32
这说明这个volatile关键字发挥了他的作用,其实不只是内嵌汇编操作栈,这种方式属于编译器无法识别的变量改变,另外更多的可能是多线程并发访问共享变量时,一个线程改变了变量的值,怎样让改变后的值对其它线程visible,一般来说用在以下几个地方。
1.中断服务程序中修改的供其他程序检测的变量需要加volatile
2.多任务环境下各任务间共享的标志应该加volatile
3.存储器映射的硬件寄存器通常也要加volatile
volatile修饰指针什么时候用?volatile修饰指针一般用在共享指针上面
例如:buffer指向一个地址,且被线程A使用,b线程修改了buffer所指的地址同时希望
A线程使用新的地址,则设置为volatile的。
多线程下的volatile
有些变量是用volatile关键字声明的。当两个线程都要用到某一个变量且该变量的值会被改变时,应该用volatile声明,该关键字的作用是防止编译器优化把变量从内存装到cpu寄存器中,如果变量被装入寄存器,那么两个线程有可能一个使用内存中的变量,一个使用寄存器中的变量,这会造成程序的错误执行,volatile的意思是让编译器每次操作变量时一定要从内存中真正取出,而不是使用已经存在的寄存器中的值,
volatile BOOL bStop = FALSE; // 在一个线程中: while( !bStop ) { ... } bStop = FALSE; return; //在另外一个线程中,要终止上面的线程循环: bStop = TRUE; while( bStop ); //等待上面的线程终止, volatile BOOL bStop = FALSE;
如果bStop不使用volatile申明,那么这个循环将是一个死循环,因为bStop已经读取到了寄存器中,寄存器中bStop的值永远不会变成FALSE,加上volatile,程序在执行时,每次均从内存中读出bStop的值,就不会死循环了。volatile这个关键字是用来设定某个对象的存储位置在内存中,而不是寄存器中。因为一般的对象编译器可能会将其的拷贝放在寄存器中用以加快指令的执行速度。