1. volatile关键字
C++ 中的 volatile 关键字和 const 对应,用来修饰变量,通常用于建立语言级别的 memory barrie。
volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。
遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
声明时语法:int volatile vInt;
当要求使用 volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。
2. volatile 指针
和 const 修饰词类似,const 有常量指针和指针常量的说法,volatile 也有相应的概念:
- 修饰由指针指向的对象、数据是 const 或 volatile 的:
const char* cpch; volatile char* vpch;
- 指针自身的值——一个代表地址的整数变量,是 const 或 volatile 的:
char* const pchc; char* volatile pchv;
注意:
-
可以把一个非volatile int赋给volatile int,但是不能把非volatile对象赋给一个volatile对象。
-
除了基本类型外,对用户定义类型也可以用volatile类型进行修饰。
-
C++中一个有volatile标识符的类只能访问它接口的子集,一个由类的实现者控制的子集。用户只能用
const_cast
来获得对类型接口的完全访问。
此外,volatile向const一样会从类传递到它的成员。
3. 多线程下的volatile
有些变量是用volatile关键字声明的。当两个线程都要用到某一个变量且该变量的值会被改变时,应该用volatile声明,该关键字的作用是防止优化编译器把变量从内存装入CPU寄存器中。
如果变量被装入寄存器,那么两个线程有可能一个使用内存中的变量,一个使用寄存器中的变量,这会造成程序的错误执行。
volatile的意思是让编译器每次操作该变量时一定要从内存中真正取出,而不是使用已经存在寄存器中的值。
4. volatile内存屏障
- volatile如何通过内存屏障保证可见性与有序性?
-
volatile修饰的变量,在每个读操作(load操作)之前都加上Load屏障,强制从主内存读取最新的数据。每次在assign赋值后面,加上Store屏障,强制将数据刷新到主内存。
-
其实说白了就是通过一个屏障让volatile的变量每次读都读主存,每次修改后立即刷到主存里面。
-
好比线程A修改 i 后立即将值刷到主存里面,后面线程B用到的时候强制从主存读取,这个时候它能看到的值是线程A修改之后的值了。也就是通过这种方式来保证多线程之间的可见性吧。
- volatile为什么保证不了原子性?
-
线程A执行 i++ 结果后将 i = 1 赋值给工作内存;但是这个时候还没来的将最新的结果刷新回主内存的时候,线程B就使用读取主内存的旧值 i = 0 ,然后执行use指令将 i = 0的值传递给线程B去进行操作了。
-
即使这个时候线程A立即将 i = 1刷入主内存,那也晚了;线程B已经使用旧值 i = 0进行操作了,像这种情况计算结果就不对了。