为什么在std :: atomic中使用了volatile限定符?


72

从我从Herb Sutter其他人那里读到的内容来看volatile,至少在C / C ++方面,您会认为并发编程是完全正交的概念。

但是,在GCC实现中,所有std::atomic的成员函数都具有volatile限定符。在Anthony Williams的实现中也是如此std::atomic

那怎么办,我的atomic<>变量是否需要volatile


+1威廉姆斯先生来这里,也许他可以露面并给出答案:)
AraK 2010年

1
我在comp.std.c ++上看到了一个有关此的问题。请记住,volatile在单个线程中进行读取和写入的保证是按顺序完成的,并且易失性对象不能在其上调用任何非易失性成员函数(就像const)。但除此之外,我对C ++中的线程一无所知。每次我尝试在标准中读到它,我开始放弃,不能够把握文本的xD间接性和逻辑的绝对数量
约翰内斯·绍布- litb

Answers:


57

为什么volatile贯穿始终使用限定词std::atomic

这样挥发性对象也可以是原子的。看到这里

相关报价是

定义功能和操作以使用易失性对象,因此应该易失的变量也可以是原子的。但是,原子性不需要挥发性限定符。

我的atomic<>变量是否需要volatile

不,原子对象不必是易失的。


注:这是为特定平台技术上同样stackoverflow.com/questions/3708160/...
苏马

易失性限定词用于防止重新排序。那就是它的作用。
麦克罗伊

4
@MichaëlRoy:volatile操作仅在wrt中进行。其他volatile访问。 atomic<T>释放,获取和seq_cst操作按顺序排序。普通的非原子变量,所以用滚动自己原子能只是 volatile不能给你同样的ACQ /相对语义没有障碍。 当编译器利用as-if规则来优化原子时,例如,防止将“冗余”写入合并到进度计数器中,volatile atomic<T>可能将来会有用。
彼得·科德斯

1
@MichaëlRoy:除了volatile sig_atomic_t,它在技术上仍然是UB ,但是大多数实际的实现int对于加载/存储来说自然是原子的(定义行为)。(我最近对此写了一个很大的答案:MCU编程-C ++ O2优化在循环时中断)仍然,如果您需要任何订购wrt。非原子变量(例如,中断处理程序编写一个普通缓冲区然后设置一个原子标志),则可以使用宽松的原子加载,并且可以atomic_signal_fence(memory_order_acquire)在主代码中防止编译时重新排序。
彼得·科德斯

1
@MichaëlRoy:是的,我确切地知道volatile在真正的编译器上会发生什么。但是您可以安全地进行信号或中断处理程序之间的交互的操作集非常有限,并且对于太宽的类型(自然而然是原子的)没有帮助。但是只要您坚持这一点,就可以。正如我在上一条评论的链接中所显示的那样,您可以使用C ++ 11轻松原子signal_fencevolatile完全有效地完成该用例的所有工作,并可以选择使用释放/获取排序signalf_fence
彼得·科德斯

79

总结其他人正确编写的内容:

C / C ++volatile用于硬件访问和中断。C ++ 11atomic<>用于线程间通信(例如,采用无锁代码)。这两个概念/用途是正交的,但是它们有重叠的要求,这就是为什么人们经常将二者混淆。

其原因atomic<>有volatile限定的功能是它具有const限定的功能,同样的原因,因为它可能在原则上的对象既atomic<>和也const和/或volatile

当然,正如我的文章所指出的,另一个令人困惑的原因是C / C ++volatile与C#/ Java不同volatile(后者基本上等效于C ++ 11 atomic<>)。


2
我将滥用这样一个事实,即您在这里对Alexandrescu的一篇文章发表意见,该文章使用该volatile标志在线程不安全的代码上产生编译时错误(使用volatile实例来锁定接口的使用并const_cast删除volatile)。获取互斥体时)。难道意义添加一个类型限定符“线程”或类似的用于此目的的语言(我只是想大声)的文章是在这里:drdobbs.com/cpp/...
大卫·罗德里格斯- dribeas

10
在Andrei的一些文章中,他真正在做的是劫持(er,我的意思是“重用”)volatile关键字,作为类型系统中一个方便的,几乎未使用的标签,他可以用作钩子来重载并获得其他效果,这有点令人困惑,因为它不是那样写的。
Herb Sutter 2010年

1
另请参阅Alexandrescu博士(不是我的名字)的后续评论和对该文章的实质性撤消
金属

15

作为常量,volatile是可传递的。如果将方法声明为,volatile则无法对其调用任何非易失性方法或其成员属性。通过使用std::atomic方法,volatile您可以允许volatile包含以下内容的类中的成员方法进行调用:std::atomic变量的。

我今天过得不好...太混乱了...也许举个例子可以帮助:

struct element {
   void op1() volatile;
   void op2();
};
struct container {
   void foo() volatile {
      e.op1();  // correct
      //e.op2();  // compile time error
   }
   element e;
};
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.