volatile只能确保不被编译器优化得去除掉;不能确保原子访问和CPU乱序执行(memory ordering);
atomic可以确保原子访问和memory ordering; 速度比volatile慢5~10倍;
C++标准里,volatile修饰的变量:
- 不允许被优化消失(optimized out);(编译器不能做任何假设和推理,都必须按部就班地与「内存」进行交互。复用寄存器中的值」就是不允许的)
- 于序列上在另一个对
volatile
对象的访问之前。(只能确保volatile变量之间的顺序,不能确保非volatile变量的先后顺序;volatile
只作用在编译器上,尽管编译器不会换序,但 CPU 的乱序执行(out-of-order execution)已是几十年的老技术了,x86和AMD64和IA64架构上还可以,但别的CPU架构就不一定了) - 另外:不同位宽的cpu也有可能效果不一样,例如32位CPU访问64位double,自己分成32位读2次期间如果有其他线程写入的话,可能就不一致了)
结论:
volatile
不能解决多线程中的问题。(历史因素——volatile关键字出现时,多核cpu和线程都还没出现的)- 按照 Hans Boehm & Nick Maclaren 的总结,
volatile
只在三种场合下是合适的。- 和信号处理(signal handler)相关的场合;
- 和内存映射硬件(memory mapped hardware)相关的场合;
- 和非本地跳转(
setjmp
和longjmp
)相关的场合。
用atomic(自带memory order的barrier功能);用mutex、lock,或interlocked等平台相关方法;
不应该用 volatile 做线程同步,因为它禁止了编译器优化,性能会下降。这种时候就应该用专门解决线程同步问题的设施工具。
多线程之间的缓存一致性:缓存一致性是由 MESI 硬件协议(状态转移图)保证的。