众所周知,std::atomic
和volatile
是不同的东西。
有两个主要区别:
可以针对std::atomic<int> a;
进行两次优化,但不能用于volatile int a;
:
a = 1; a = 2;
可由a = 2;
a = 1; local = a;
可由a = 1; local = 1;
跨原子/易失性操作重新排序普通读/写:
volatile int a;
任何易失性读/写操作都不能重新排序。但附近的普通读/写仍然可以在易失性读/写周围重新排序。std::atomic a;
根据用于原子操作的内存屏障重新排序附近的普通读/写a.load(std::memory_order_...);
即。 volatile
没有引入内存栅栏,但std::atomic
可以执行此操作。
如文章中所述:
例如,std::atomic
应该用于并发多线程程序(CPU-Core&lt; - &gt; CPU-Core),但volatile
应该用于访问Mamory Mapped Regions on设备(CPU-Core&lt; - &gt; Device)。
但是如果需要,它们都具有不寻常的语义,并且具有无锁编码所需的任何或所有原子性和/或排序保证,即如果需要volatile std::atomic<>
,则需要以下几个原因:
例如:
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;
示例:
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
为此,Herb Sutter谈到volatile atomic<T>
,2009年1月8日:http://www.drdobbs.com/parallel/volatile-vs-volatile/212701484?pgno=2
最后,表达一个具有异常语义且具有异常语义的变量 所需的任何或所有原子性和/或排序保证 无锁编码,只有ISO C ++ 0x草案标准提供了直接 拼写它的方法:挥发性原子。
但现代标准C ++ 11(不是C ++ 0x草案),C ++ 14和C ++ 17保证volatile atomic<T>
具有两种语义(volatile +原子)?
volatile atomic<T>
是否保证来自volatile和atomic的最严格保证?
volatile
中一样:避免融合操作和常量传播,如问题开头所述std::atomic
中一样:引入内存栅栏以提供排序,溢出和原子化。我们可以从reinterpret_cast
到volatile int *ptr;
进行volatile std::atomic<int>*
吗?
答案 0 :(得分:5)
是的,确实如此。
第29.6.5节,“原子类型操作要求”
许多操作都是挥发性合格的。 “易失性设备寄存器”语义在标准中没有改变。此限定意味着在将这些操作应用于易失性对象时会保留波动性。
我检查了2008年到2016年的工作草案,所有这些都是相同的文字。因此它应该应用C ++ 11,C ++ 14和C ++ 17。
答案 1 :(得分:0)
我们可以从
volatile int *ptr;
到volatile std::atomic<int>*
重新解释吗?
当且仅当ABI指出两种类型(此处为int
和std::atomic<int>
)具有相同的表示形式和限制时,您才能进行此类转换:大小,对齐方式和可能的位模式;相同的位模式含义相同。
所有易失性都与ABI直接相关:具有易失性的变量必须在序列点处具有规范的ABI表示形式,并且对易失性对象的操作仅假定它们遵循其ABI要求,而没有其他要求。因此,每当在C或C ++中使用volatile时,您都可以依赖于语言标准或平台ABI。
(我希望这个答案不会被删除,因为有些人鄙视易变的语义,并依赖于ABI和特定于平台的概念。)