书中举了一个很简单的例子:
int global_x = 0; // 两个线程共享的全局变量.
Thread1: // 线程 1 的定义 Thread2: // 线程 2 的定义
lock(); lock();
global_x++; global_x++;
unlock(); unlock();
看上去,因为线程 1 和线程 2 在访问 global_x 时都使用了 lock() 和 unlock() 保护,因此 global_x++ 的行为不会被并发破坏,所以在线程 1 和线程 2 结束之后,global_x 的值似乎一定是 2。但其实,这么理所当然的猜测有可能是错误的。解释如下:
- [Thread1] 读 global_x 值到寄存器 R[1]
- [Thread1] R[1]++ (R[1]=1)
- [Thread2] 读 global_x 值到寄存器 R[2]
- [Thread2] R[2]++ (R[2]=1)
- [Thread2] 将寄存器 R[2]的值写回 global_x (global_x=1)
- [Thread1] 将寄存器 R[1]的值写回 global_x (global_x=1)
出现这样的问题,是因为编译器为了提高global_x的访问速度,将global_x的值放到了某个寄存器里,这就导致了所谓过度优化的问题。
书中给出的为了阻止过度优化的方法是使用 volatile 关键字。(注:这里的 volatile 仅指 C/C++的关键字,不要和 java 中的搞混)
我的理解是volatile从来不是多线程中需要的,靠操作系统提供的同步原语应该就足够了。
相关链接:
pthread locks implement memory barriers that will ensure that cache effects are made visible to other threads. You don't need volatile to properly deal with the shared variable i if the accesses to the shared variable are protected by pthread mutexes.