常规内存的特征是,如果你向某个内存位置写入了值,该值会一直保留在那里,直到它被覆盖为止。所以,如过我有一个常规的 int
变量:
int x;
且编译器看到了对其实施了以下序列的操作:
auto y = x;
y = x;
编译器就可以通过消除 y
的赋值操作来优化生成的代码。
特种内存是另一回事,最常见的特种内存是用于内存映射I/O的内存。这种内存的位置实际上是用于与外部设备通信。在此情况下,再次考虑看似冗余的代码:
auto y = x;
y = x;
比如温度传感器报告的值,则 x
的第二次读取操作并非多余,因为两次读取之间,温度可能已经改变。
而 volatile
就是告诉编译器,正在处理的是特种内存,不要在此内存上的操作做任何优化。所以,如果 x
对应于特种内存,则它应该加上 volatile
修饰:
volatile int x;
std::atomic
变量并不适用于这种情况:
std::atomic<int> x;
auto y = x;
y = x;
x = 10;
x = 20;
会被优化成这样:
auto y = x;
x = 20;
以下两个语句在 x
是 std::atomic
类型对象时都不能通过编译:
auto y = x;
y = x;
因为 std::atomic
的复制操作被删除了。因为硬件层面无法完成在单一的原子操作中读取x
并写入y
。
从x
中取值并置入y
时可以实现的。如果想实现上面的操作,必须这样写:
std::atomic<int> y(x.load());
y.store(x.load());
编译器优化时只会读取一次x
的值,存入寄存器,将寄存器中的值赋值给y
。而在处理特种内存时需要避免这种优化。
std::atomic
对于并发程序设计很有用,但不能用于访问特种内存。volatile
对于访问特种内存有用,但不能用于并发程序设计。
两者也可以同时使用,表示操作是原子的,并且不可以被优化掉:
volatile std::atomic<int> val;
这对于有多个线程同时访问的内存映射I/O位置,就可能会是有用的。