易变量被优化了吗?

时间:2014-05-20 13:30:10

标签: c++ multithreading volatile

我有一个后台线程,它在状态变量done上循环。当我想停止线程时,我将变量done设置为true。但显然这个变量从未设定过。我知道编译器可能会对其进行优化,因此我标记了done volatile。但这似乎没有任何影响。注意,我并不担心竞争条件,所以我没有使它atomic或使用任何同步结构。如何让线程在每次迭代时都不跳过测试变量?或者问题完全是另一回事? done最初为false

struct SomeObject
{
    volatile bool done_;
    void DoRun();
};

static void RunLoop(void* arg)
{
   if (!arg)
      return;

   SomeObject* thiz = static_cast<SomeObject*>(arg);
   while( !(thiz->done_) ) {
      thiz->DoRun();
   }
   return;
}

2 个答案:

答案 0 :(得分:3)

volatile在C ++中没有任何多线程含义。它是来自C的保留,用作信号处理程序触及的sig_atomic_t标志的修饰符以及对内存映射设备的访问。没有语言强制要求C ++函数重新访问内存,这会导致竞争条件(读者从未打扰过两次检查&#34;优化&#34;),其他人注意到这一点。

使用std::atomic(来自C ++ 11或更高版本)。

它可以,而且通常是无锁的:

struct SomeObject {
  std::atomic_bool done_;
  void DoRun();
  bool IsDone() { return done_.load(); }
  void KillMe() { done_.store(true); }
};

static void RunLoop(void *arg) {
  SomeObject &t = static_cast<SomeObject &>(*arg);

  cout << t.done_.is_lock_free(); // some archaic platforms may be false

  while (!t.IsDone()) {
    t.DoRun();
  }
}

load()store()方法强制编译器至少在每次迭代时检查内存位置。对于x86[_64]SomeObject实例的done_成员的缓存行将被缓存并在本地检查,没有锁定,甚至原子/锁定内存读取原样。如果你做的事情比单向标志集更复杂,你需要考虑使用明确的内存栅栏等等。

Pre-C ++ 11没有多线程内存模型,因此您必须依赖具有特殊编译器权限(如pthread)的第三方库,或使用特定于编译器的功能来获取等效内容。 /强>

答案 1 :(得分:1)

这与您在msvc 2010中的预期效果相同。如果我删除volatile,它将永远循环。如果我留下挥发性,它就有效。这是因为microsoft像你期望的那样对待volatile,这与iso不同。

这也有效:

struct CDone {
    bool m_fDone;
};

int ThreadProc(volatile CDone *pDone) {
}

以下是MSDN的说法:

http://msdn.microsoft.com/en-us/library/12a04hfd.aspx

声明为volatile的对象不会在某些优化中使用,因为它们的值可能随时更改。系统始终在请求时读取volatile对象的当前值,即使前一条指令要求来自同一对象的值也是如此。

此外,对象的值在分配时立即写入。

符合ISO标准:

如果您熟悉C#volatile关键字,或者熟悉早期版本的Visual C ++中volatile的行为,请注意C ++ 11 ISO标准的volatile关键字是不同的,并且当/ volatile:指定iso编译器选项。 (对于ARM,默认情况下指定)。 C ++ 11 ISO标准代码中的volatile关键字仅用于硬件访问;不要将它用于线程间通信。对于线程间通信,请使用来自C ++标准模板库的std :: atomic等机制。