C++中volatile
volatile只保证其“可见性”,不保证其“原子性”。
执行count++;这条语句由3条指令组成:
(1)将 count 的值从内存加载到 cpu 的某个 寄存器r;
(2)将 寄存器r 的值 +1,结果存放在 寄存器s;
(3)将 寄存器s 中的值写回内存。
所以,如果有多个线程同时在执行 count++,在某个线程执行完第(3)步之前,其它线程是看不到它的执行结果的。(这里有疑惑:线程同时执行count++,为了保证其原子性,为何不加mutex lock?而是寻求volatile?)
在没有volatile的时候,执行完count++,执行结果其实是写到CPU缓存中,没有马上写回到内存中,后续在某些情况下(比如CPU缓存不够用)再将CPU缓存中的值flush到内存。因为没有存到内存里,其他线程是不能及时看到执行结果的。
在有volatile的时候,执行完count++,执行结果写入缓存中,并同时写入内存中,所以可以保证其它线程马上看到执行的结果。
但是,volatile 并没有保证原子性,在某个线程执行(1)(2)(3)的时候,volatile 并没有锁定 count 的值,也就是并不能阻塞其他线程也执行(1)(2)(3)。可能有两个线程同时执行(1),所以(2)计算出来一样的结果,然后(3)存回的也是同一个值。
考虑下面一段代码:
1 int some_int = 100; 2 3 while(some_int == 100) 4 { 5 //your code 6 }
这时候,编译器会优化代码为:
1 //your code
因为编译器认为some_int没被改变过,一直是100。但是在多线程时,如果执行完第一行,但是还没执行到第三行时,另一个线程修改了some_int,while就不能进入循环了。加了volatile后,阻止了编译器优化,每次读到some_int会从内存中读取,而不是本线程的寄存去(当然这会损失效率)。这就是volatile的作用。
一句话总结:volatile保证线程能读到最新的数据,因为是从内存中读取,且存入内存中。而不是线程各自的寄存器中读写。
疑问:
我知道how volatile works,但是为何要保证其“可见性”呢?有一直需求可能是某个线程在监视某个变量,而这个变量可以被很多线程调用。我理解的对吗?如果是这样的话,多线程之间用static可以么,只存一份值,难道寄存器也会优化吗?
答:
既然static已经是全局唯一了,那应该就是线程间共享,因此一个线程改变它其它线程都能看到,可惜这并不是事实,为了提高优化度和运行速度,一个线程修改这个变量是不一定立即写回内存让其它线程看到的,这时候就需要用volatile强制要求每次改变都写回内存了。
天行健,君子当自强不息