众所周知, std::atomic
和 volatile
是不同的东西 .
有两个主要区别:
-
两个优化可以用于
std::atomic<int> a;
,但不能用于volatile int a;
: -
融合操作:
a = 1; a = 2;
可由a = 2;
上的编译器替换 -
常量传播:
a = 1; local = a;
可由a = 1; local = 1;
上的编译器替换 -
跨原子/易失性操作重新排序普通读/写:
-
for
volatile int a;
任何volatile-read / write-operations都无法重新排序 . 但附近的普通读/写仍然可以在易失性读/写周围重新排序 . -
for
std::atomic a;
根据用于原子操作的内存屏障对附近的普通读/写进行重新排序a.load(std::memory_order_...);
即 volatile
不介绍内存栅栏,但 std::atomic
可以做到 .
正如文章中所描述的那样:
-
Herb Sutter,2009年1月8日 - 第1部分:http://www.drdobbs.com/parallel/volatile-vs-volatile/212701484
-
Herb Sutter,2009年1月8日 - 第2部分:http://www.drdobbs.com/parallel/volatile-vs-volatile/212701484?pgno=2
例如, std::atomic
应该用于并发多线程程序(CPU-Core < - > CPU-Core),但 volatile
应该用于访问设备上的Mamory Mapped Regions(CPU-Core < - > Device) .
但是如果需要,两者都具有不寻常的语义,并且具有无锁编码所需的任何或所有原子性和/或排序保证,即如果需要 volatile std::atomic<>
,则需要以下几个原因:
- ordering :防止普通读/写的重新排序,例如,从CPU-RAM读取,使用Device DMA-controller写入数据
例如:
char cpu_ram_data_written_by_device[1024];
device_dma_will_write_here( cpu_ram_data_written_by_device );
// physically mapped to device register
volatile bool *device_ready = get_pointer_device_ready_flag();
//... somewhere much later
while(!device_ready); // spin-lock (here should be memory fence!!!)
for(auto &i : cpu_ram_data_written_by_device) std::cout << i;
- spilling :CPU写入CPU-RAM,然后从该存储器读取设备DMA控制器:https://en.wikipedia.org/wiki/Register_allocation#Spilling
例:
char cpu_ram_data_will_read_by_device[1024];
device_dma_will_read_it( cpu_ram_data_written_by_device );
// physically mapped to device register
volatile bool *data_ready = get_pointer_data_ready_flag();
//... somewhere much later
for(auto &i : cpu_ram_data_will_read_by_device) i = 10;
data_ready=true; //spilling cpu_ram_data_will_read_by_device to RAM, should be memory fence
- atomic :保证volatile操作是原子的 - 即它将由单个操作而不是多个操作组成 - 即一个8字节操作而不是两个4字节操作
为此,Herb Sutter谈到 volatile atomic<T>
,2009年1月8日:http://www.drdobbs.com/parallel/volatile-vs-volatile/212701484?pgno=2
最后,为了表达一个变量,它既有不寻常的语义,又具有无锁编码所需的任何或全部原子性和/或排序保证,只有ISO C 0x草案标准提供了拼写它的直接方法:volatile atomic .
但 modern 标准C 11(不是C 0x草案),C 14和C 17是否保证 volatile atomic<T>
具有两种语义(易失性原子)?
volatile atomic<T>
能保证挥发性和原子性的最严格保证吗?
-
与
volatile
中一样:避免融合操作和常量传播,如问题开头所述 -
与
std::atomic
中一样:引入内存栅栏以提供排序,溢出和原子化 .
我们可以 volatile int *ptr;
从 volatile int *ptr;
到 volatile std::atomic<int>*
吗?
1 回答
是的,它确实 .
第29.6.5节,"Requirements for operations on atomic types"
我检查了2008年到2016年的工作草案,所有这些都是相同的文字 . 因此,它应该应用C 11,C 14和C 17 .