如果跨线程共享变量,将变量标记为volatile是否有用?

Lig*_*ica 4 c++ multithreading volatile c++11

注意!

我显然没有向这里的每个人清楚地表明我的观点,这令人非常沮丧.我的目标是打消那种volatile实际上是无操作的神话,它什么都不做.我并没有试图说它应该被使用,它是必不可少的,它不是多余的,等等.

我已经表明volatile它仍然可以做一件事.我承认在某些情况下它是多余的,并且多线程示例是一个糟糕的选择.

我也没有试图隐瞒我的答案的初始修订包含错误这一事实.但这个Q&A甚至没有达到其预期目的.为此,我认为是时候把它扔掉了.

感谢Kerrek和TC的见解.我只是不认为他们的回答符合我想问的问题.我很确定这是我的错,因为它很糟糕.

所以我放弃了!并将其作为问题的副本而不是它的意图,但它被解释为.

干杯! (&hth.)

我正在写一个线程中的变量并在另一个线程中读取它.我被告知这对我volatile来说完全无用,除非我正在使用硬件,否则我不需要在这个时代使用它.

int x = 0;
void thread1()
{
   while (true) {
      sleep(1);
      if (x > 0)
         break;
   }
}

void thread2()
{
   while (true) {
      sleep(1);
      x++;
   }
}
Run Code Online (Sandbox Code Playgroud)

volatile在这种情况下,我可以获得任何收益吗?
如果x不是一个简单int的类而是一个类类型怎么样?

Ker*_* SB 20

您被正确告知,volatile对于线程间通信没有用.不要将它用于此目的.它不提供同步,并为您的代码留下数据争用.相反,在正确同步对共享状态的访问时,您不需要volatile.

Do Does Your Mean的正确代码使用原子变量作为共享状态或使用互斥锁保护共享状态,并且所有线程都将正确地观察共享状态.例如:

#include <atomic>

std::atomic<int> x = 0;

void thread1()
{
   while (true) {
      sleep(1);
      if (x > 0)
         break;
   }
}

void thread2()
{
   while (true) {
      sleep(1);
      x++;
   }
}
Run Code Online (Sandbox Code Playgroud)

在任何时候都没有必要volatile.

请注意,一个线程中volatile可能有用,可以强制评估一个没有副作用的循环:

// Spend some time
for (volatile int i = 0; i != LARGE_NUMBER; ++i)
{ /* do nothing */ }

// or even:
for (int i = 0; const_cast<volatile int &>(i) != LARGE_NUMBER; ++i) {}
Run Code Online (Sandbox Code Playgroud)

  • @LightnessRacesinOrbit它们不是跨线程共享所必需的.它们不足以跨线程共享.那么为什么要把它们放进去,而不仅仅是复杂化和悲观化? (6认同)
  • @LightnessRacesinOrbit除了你添加一个误解之外.提出的问题是关于跨线程的易失性访问,并且您(或其他任何人)尚未发布在此上下文中必要的示例. (4认同)
  • @MattMcNabb不,你不要把它标记为`volatile`.你只需编写适当的同步,这就足够了.(在`<atomic>`之外,没有标准的库类具有`volatile`成员函数,所以如果你写一个`volatile std :: string`,你可以用它做很少的事情.) (4认同)
  • @MattMcNabb同步操作.(给定write-unlock-lock-read,前两个操作发生在一个线程上,后两个操作发生在另一个线程上,unlock*与*lock同步,因此write*发生在*read之前,并且假设没有介入写入,通过[intro.multithread]/p15,读取必须读取写入的内容.) (3认同)
  • @MattMcNabb:我认为你是这篇文章的主要受害者.不,`volatile`不是*必要的; C++内存模型和对库实现的后续要求保证使用互斥锁提供正确的可见性. (3认同)
  • 还有一件事可以阻止这样的写入被优化 - [intro.execution]/1中的as-if规则.为了使用,比方说,来自另一个线程的字符串变量,要么需要从写入发生的转换单元传递指针或引用它(在这种情况下,实现可以看到并且不应该假定写入没有)或者变量必须是全局的并且在另一个翻译单元(实现不一定能看到)中声明为extern,并且在extern情况下,实现必须证明写入是不可观察的以便优化. (3认同)
  • 现在,由于原始代码不使用原子或volatile变量,我们应该将注意力集中在[intro.multithread]/27:"实现可能假设任何线程最终会执行以下操作之一: - 终止, - 调用库I/O函数, - 访问或修改易失性对象,或 - 执行同步操作或原子操作.原始代码不执行这些操作,因此实现可能会根据该假设进行操作.使所谓的共享变量volatile不会使它跨线程工作,但会阻止对所述假设采取行动. (3认同)
  • @LightnessRacesinOrbit:因为它几乎完全强化了一种极其普遍和危险的错误观念.老实说,我几乎无法相信你发布了这个.如果你想讨论`volatile`的用法,我会说一个像"挥发无用吗?"或"挥发的影响是什么?"这样的问题.会更有帮助.在任何情况下都要保持多线程的东西! (2认同)