语义
volatile
Indicates that a variable can be changed by a background routine.
Keyword volatile is an extreme opposite of const. It indicates that a variable may be changed in a way which is absolutely unpredictable by analysing the normal program flow (for example, a variable which may be changed by an interrupt handler). This keyword uses the following syntax:
volatile data-definition;
Every reference to the variable will reload the contents from memory rather than take advantage of situations where a copy can be in a register.
概括一下: volatile 指变量是易变的,它可能随时被别的程序流修改,被volatile 修饰的变量不能储存在寄存器中。
语法
修饰变量
volatile int a = 0;
修饰指针
int * volatile p;
修饰指针指向的地址
volatile int * p;
修饰指针及指向的地址
volatile int * volatile p;
修饰结构体成员
通过一个non-volatile的结构体指针,去访问被定义为volatile的结构体成员,C标准没有对编译器在这种情况下的行为做规定。可能将其成员作为volatile 变量,也可能作为普通变量。
修饰指向结构体指针
将一个non-volatile的结构体地址赋给一个 volatile的指针,这样对volatile指针所指结构体的使用都会被编译器认为是volatile的,即使原本那个对象没有被声明为 volatile。
实现机制
根据volatile的语义可以理解为: 使用它是为了保证顺序一致性。因此对volatile的实现一般和以下三点有关
1 不会进行编译器优化。
编译器遇到volatile 描述的变量禁止编译器优化,一般优化方式有下面几种:
- 变量改为寄存器变量
- 指令省略
- 指令重排
- 其他优化
2 cpu不会进行指令乱序和并发
3 修改数据后立即同步各缓存(内存),保证缓存(内存)一致
1、2保证了执行顺序与程序顺序是一致的; 3保证了修改数据对所有共享者是立即可见的。123一起即可保证顺序一致性。C编译器通常可以实现 1,但并不保证2,3。
对比Java的volatile,Jvm实现了123: 参考https://blog.csdn.net/lc13571525583/article/details/90345760
C/C++实现
转自http://baiy.cn/doc/cpp/advanced_topic_about_multicore_and_threading.htm
C/C++ 中的 volatile 关键字提供了以下保证:
|
volatile *不* 提供如下保证
|
原子操作要求做到以下保证:
|
可见,使用 volitale 关键字并不足以保证操作的原子语义。 volitale 关键字的主要设计目的是支持 C/C++ 程序与内存映射设备间的通信。但这并不是说 volitale 关键字对原子操作没有任何帮助:
-
对于一个被声明为 volitale 类型的原子量来说,如果所有写操作都是原子的,并且完成了必要的 cache 同步,那么读取该原子量时就不必再次对总线上锁。
这是因为 volitale 关键字保证了对该变量的读操作起码是在当前 CPU cache 中完成的(即:该变量不会被优化到寄存器中)。与此同时,对该变量的所有写操作都已保证原子地完成了所有 CPU 间的 cache 同步以及主存同步操作。所以读操作不管在主存还是当前系统中任意一个 CPU 的 cache 上发生,读到的都是一致的数据。
在这里,volitale 关键字配合原子量的写入操作一起实现了一个典型的读者/写者同步模型:所有写操作和 cache 同步都保证被原子、互斥地完成;读操作则可以被并发地实现。这也是 Windows API 中没有提供类似 'AtomicLoad' 式语义操作的原因。
当然,这里还有一个隐含的附加条件,就是 CPU 必须能够在一个操作中读入这个原子量。这个条件通常可以忽略,因为超出 CPU 位宽的数据类型通常无法被实现成原子量类型。