Monitor.Wait - while还是if?

5

我目前正在学习多线程考试,我阅读了albahari的优秀线程文章。在监视器使用方面,我有一个问题 - 为什么在这里使用循环而不是if语句?

lock (_locker)
{
  while (!_go) //why while and not if?
    Monitor.Wait (_locker);  // _lock is released
  // lock is regained
  ...
}

我认为,一个if语句就足够了。

恐怕,我并没有完全理解这篇文章。

//编辑 示例代码:

class SimpleWaitPulse
{
  static readonly object _locker = new object();
  static bool _go;

  static void Main()
  {                                // The new thread will block
    new Thread (Work).Start();     // because _go==false.

    Console.ReadLine();            // Wait for user to hit Enter

    lock (_locker)                 // Let's now wake up the thread by
    {                              // setting _go=true and pulsing.
      _go = true;
      Monitor.Pulse (_locker);
    }
  }

  static void Work()
  {
    lock (_locker)
      while (!_go)
        Monitor.Wait (_locker);    // Lock is released while we’re waiting

    Console.WriteLine ("Woken!!!");
  }
}

1
"If" 只检查一次。 "while" 循环将继续检查。 "if" 不等待。 "while" 等待。 - DOK
嗨DOK - 謝謝你的解釋。但我認為,Monitor.Wait操作只會執行一次。它將等待Pulse信號。所以我看不到任何理由需要while :-( - bitsmuggler
专业人士,这取决于您想等待事件发生的次数。通常,在锁定后内存中会进行某种修改,如果您只想“观察”一次修改,则可以使用if(例如,您发送消息并等待单个响应)。如果您想持续“观察”修改,则在while循环中执行它(即,您正在监视大量消息的消息流)。 - Kiril
根据更新,最好使用while循环,因为您始终会在输入后执行工作。使用if语句将导致工作的单次执行,并且在此之后的任何输入都将被忽略(即不会执行任何工作)。 - Kiril
嗨Lirik,我不理解的是,如果线程到达等待方法。我将一直等待,直到另一个线程执行脉冲方法。因此,我假设(在任何情况下)不再需要更多迭代。 - bitsmuggler
显示剩余3条评论
5个回答

5

这仅仅取决于情况。在这种情况下,代码只是等待_go变为true

每次触发_locker时,它将检查_go是否已设置为true。如果_go仍为false,则它将等待下一个触发。

如果使用if而不是while,它只会等待一次(如果_go已经是true,则根本不会等待),然后在脉冲后继续执行,无论_go的新状态如何。

因此,您如何使用Monitor.Wait()完全取决于您的具体需求。


3

这取决于具体情况。但首先,我们需要澄清监视器的工作原理。当一个线程通过Monitor.Pulse()信号通知另一个线程时,通常不能保证被通知的线程会立即运行。这意味着,在被通知的线程运行之前,其他线程可能会运行并改变被通知线程可以继续执行的条件。因此,被通知的线程在唤醒后仍需要检查是否安全继续执行(即while循环)。然而,某些罕见的同步问题允许您做出这样的假设:一旦线程被通知唤醒(即Monitor.Pulse()),没有其他线程能够改变被通知线程可以安全继续执行的条件(即if条件)。


2
我写了一篇文章,可能会对这里有所帮助:等待和脉冲的解密 事情比表面上看起来要复杂得多。

1
我有一个关于监视器使用的问题 - 为什么这里使用循环而不是if语句?
在使用Pulse和Wait时,有一个众所周知的规则,即在怀疑时优先选择while而不是if。显然,在这种情况下,任何一种方法都可以工作,但在几乎所有其他情况下,都需要使用while。实际上,几乎没有(如果有的话)使用while循环会产生错误结果的情况。这就是这个通用规则的基础。作者使用while循环是因为他试图坚持经过验证的模式。他甚至在同一篇文章中提供了模板。以下是它:
lock (_locker)
  while ( <blocking-condition> )
    Monitor.Wait (_locker);

0
使用Monitor.Wait编写正确代码的最简单方法是假设系统将其视为“咨询性的”,并假设系统可能会在任何时候随意唤醒任何等待线程,而不考虑是否已调用Pulse。当然,系统通常不会这样做,但如果程序正确地使用WaitPulse,则它的正确性不应受到任何原因导致Wait调用任意提前退出的影响。基本上,应将Wait视为告诉系统“除非或直到其他人调用Pulse,否则继续执行此处将是浪费时间的手段”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接