一、概念
volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统,硬件或者其他线程等。
遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。声明时语法:volatile int vInt; 当要求使用 volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。
volatile int i=10;
int a = i;
...
// 其他代码,并未明确告诉编译器,对 i 进行过操作
int b = i;
volatile int i=10;
int a = i;
...
// 其他代码,并未明确告诉编译器,对 i 进行过操作
int b = i;
volatile 指出 i 是随时可能发生变化的,每次使用它的时候必须从 i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在 b 中。
而优化做法是,由于编译器发现两次从 i读数据的代码之间的代码没有对 i 进行过操作,它会自动把上次读的数据放在 b 中。而不是重新从 i 里面读。
二、特性
- 易变性
volatile int *p = /* ... */;
int a, b;
a = *p;
b = *p;
不使用volatile时,b会直接使用a=p后存在于寄存器中的p值,使用volatile时,每次都会从内存中重新读取
- 不可优化
int a, b, c;
a = 1, b = 2, c = 3;
cout << a << b << c;
变量a,b,c没有被volatile修饰时,会被优化成三个常量,使用volatile后不可优化,需要将三个变量从内存读入寄存器中,再进行输出
- 顺序性
volatile int something = 0;
volatile int flag = false;
Thread1()
{
something = 1;
flag = true;
}
Thread2()
{
if (flag == true)
{
assert(something == 1);
other things;
}
}
上面的代码看似能够保证something = 1和flag = true的执行顺序,也能确保Thread2在flag == true时,即Thread1执行完something = 1后,再执行other things。但由于编译后的代码最终通过CPU来执行,CPU为了提高代码运行的效率,也会对代码的执行顺序进行调整。
可见三volatile解决的问题