使用volatile在.NET中的线程之间进行单向通信

时间:2011-11-03 00:52:19

标签: c# .net multithreading synchronization

我已经阅读了相关数量的相关帖子,我想我终于明白了volatile关键字在C#中是如何运作的。我仍然想在这里问一下,以确保我理解正确的概念。请考虑以下代码:

class ThreadWrapper
{
  public bool Enabled { get; set; }

  private void ThreadMethod()
  {
    while (Enabled) { // ... Do work }
  }
}

根据我的理解,如果另一个线程要设置Enabled=false,原始线程可能不会立即(或者可能永远?)看到此更改。为了确保编译器不优化对Enabled属性的访问并依赖缓存值,更改代码如下所示应该解决问题。

  public bool Enabled { get {return mEnabled;} set {mEnabled=value;} }
  private volatile bool mEnabled;

现在,只要读取Enabled值,就可以保证获得最新值,对吗?如果是这样,我应该能够将它用作一个简单的信号或标志(就像我上面所说的那样)。

作为另一个例子,请考虑以下事项:

class C
{
  private volatile int i;
  private volatile bool enabledA;
  private volatile bool enabledB;

  void ThreadA()
  {
    i = 0; // Reset counter
    while (enabledA)
    {
      // .. Do some other repeated work
      if (i > 100)
      {
        // .. Handle threshold case
      }
    }
  }

  void ThreadB()
  {
    while (enabledB)
    {
      // .. Do work
      if (condition) // Some condition occurs that we want to count
      {
        i++;
      }
    }
  }
}

对于像这样的简单线程间通信,这里volatile是否令人满意?我理解i++不是原子操作,但是如果一个线程正在进行所有递增而另一个线程只是想读取它以查看它是否达到某个阈值,那么我们还可以吗?

编辑:我当然在之后找到了another similar question with detailed answers

2 个答案:

答案 0 :(得分:5)

我相信你是对的。

MSDN's article on the volatile keyword在其示例中使用了相同的技术。

答案 1 :(得分:3)

是的,这应该是安全的,并且是volatile关键字的预期用途之一。但请注意,您不应仅仅循环来测试条件;如果您只想等待它更改,请使用适当的同步原语以避免浪费CPU时间。另请注意,此技术仅适用,因为您只有一个线程写入;如果多个线程试图递增计数器,则可能会失去计数器命中(因为读取 - 修改 - 写入不是原子的)。