含义
对于程序员来说,程序本身的任何行为都必须是可预期的。那么,在程序当中,什么才叫 volatile 呢?这个问题的答案也很简单:程序可能受到程序之外的因素影响。
多线程
volatile 在 c90 时代就有了,当时 c 标准库还没有对并发程序支持,volatile 不是为了解决线程安全诞生的。volatile 修饰的变量将会读写到内存的一块区域,而不是一块 cpu 的高速缓存中。它保证了各个线程读取的是同一块位置。
当多线程的程序用同一个指针去修改值之后,需要CPU去讲L1中的数据同步到L2,然后从L2再同步到L1。此时如果用两个指针去修改同一个地址,那么两个指针不一定会去读同一块地方。只有变量被volatile修饰了,该变量所在的缓存行才被赋予缓存一致性的校验功能。
int a = 0;
int *p = &a;
volatile int *q = &a;
加了 volatile 修饰的 q 指针,我们每次通过 q 这个途径去访问 a 对象的时候,都会直接去内存中去读。
解决代码合并
对 volatile 对象的访问,有编译器优化上的副作用,不允许被优化消失(optimized out),于序列上在另一个对 volatile 对象的访问之前。
#include <iostream>
int main() {
int a = 10000;
a+= 1000;
a+= 2000;
std::cout << a;
return 0;
}
// gcc -S main.cpp -o main.s -O2
movl $13000, %edx
#include <iostream>
int main() {
volatile int a = 10000;
a+= 1000;
a+= 2000;
std::cout << a;
return 0;
}
// gcc -S main.cpp -o main.s -O2
movl $10000, 44(%rsp)
movl 44(%rsp), %eax
movq .refptr._ZSt4cout(%rip), %rcx
addl $1000, %eax
movl %eax, 44(%rsp)
movl 44(%rsp), %eax
addl $2000, %eax