Skip to content

Latest commit

 

History

History
80 lines (53 loc) · 2.16 KB

File metadata and controls

80 lines (53 loc) · 2.16 KB

条款40:对并发使用std::atomic,对特种内存使用volatile

常规内存的特征是,如果你向某个内存位置写入了值,该值会一直保留在那里,直到它被覆盖为止。所以,如过我有一个常规的 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;

以下两个语句在 xstd::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位置,就可能会是有用的。