标准C ++ 11是否保证`volatile atomic <t>`具有语义(volatile + atomic)?

时间:2016-11-01 19:14:58

标签: c++ multithreading c++11 concurrency volatile

众所周知,std::atomicvolatile是不同的东西。

有两个主要区别:

  1. 可以针对std::atomic<int> a;进行两次优化,但不能用于volatile int a;

    • 融合操作:a = 1; a = 2;可由a = 2;
    • 上的编译器替换
    • 常量传播:a = 1; local = a;可由a = 1; local = 1;
    • 上的编译器替换
  2. 跨原子/易失性操作重新排序普通读/写:

    • 对于volatile int a;任何易失性读/写操作都不能重新排序。但附近的普通读/写仍然可以在易失性读/写周围重新排序。
    • for std::atomic a;根据用于原子操作的内存屏障重新排序附近的普通读/写a.load(std::memory_order_...);
  3. 即。 volatile没有引入内存栅栏,但std::atomic可以执行此操作。

    如文章中所述:

    例如,std::atomic应该用于并发多线程程序(CPU-Core&lt; - &gt; CPU-Core),但volatile应该用于访问Mamory Mapped Regions on设备(CPU-Core&lt; - &gt; Device)。

    但是如果需要,它们都具有不寻常的语义,并且具有无锁编码所需的任何或所有原子性和/或排序保证,即如果需要volatile std::atomic<>,则需要以下几个原因:

    • 排序:防止普通读/写的重新排序,例如,从使用Device DMA控制器写入数据的CPU-RAM读取

    例如:

    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
    
    • atomic :保证volatile操作是原子的 - 即它将由单个操作而不是多个操作组成 - 即一个8字节操作而不是两个4字节操作< / LI>

    为此,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的最严格保证?

    1. volatile中一样:避免融合操作和常量传播,如问题开头所述
    2. std::atomic中一样:引入内存栅栏以提供排序,溢出和原子化。
    3. 我们可以从reinterpret_castvolatile int *ptr;进行volatile std::atomic<int>*吗?

2 个答案:

答案 0 :(得分:5)

是的,确实如此。

第29.6.5节,“原子类型操作要求”

  

许多操作都是挥发性合格的。 “易失性设备寄存器”语义在标准中没有改变。此限定意味着在将这些操作应用于易失性对象时会保留波动性。

我检查了2008年到2016年的工作草案,所有这些都是相同的文字。因此它应该应用C ++ 11,C ++ 14和C ++ 17。

答案 1 :(得分:0)

  

我们可以从volatile int *ptr;volatile std::atomic<int>*重新解释吗?

当且仅当ABI指出两种类型(此处为intstd::atomic<int>)具有相同的表示形式和限制时,您才能进行此类转换:大小,对齐方式和可能的位模式;相同的位模式含义相同。

所有易失性都与ABI直接相关:具有易失性的变量必须在序列点处具有规范的ABI表示形式,并且对易失性对象的操作仅假定它们遵循其ABI要求,而没有其他要求。因此,每当在C或C ++中使用volatile时,您都可以依赖于语言标准或平台ABI。

(我希望这个答案不会被删除,因为有些人鄙视易变的语义,并依赖于ABI和特定于平台的概念。)