不明白需要Monitor.Pulse()

根据MSDN , Monitor.Wait()

释放对象的锁定并阻止当前线程,直到它重新获取锁定。

但是,我所读到的关于Wait()和Pulse()的所有内容似乎表明仅仅释放另一个线程上的锁是不够的。 我需要先调用Pulse()来唤醒等待的线程。

我的问题是为什么? 在Monitor.Enter()上等待锁定的线程只是在它被释放时获取它。 没有必要“唤醒他们”。 它似乎打败了Wait()的有用性。

例如。

 static object _lock = new Object(); static void Main() { new Thread(Count).Start(); Sleep(10); lock (_lock) { Console.WriteLine("Main thread grabbed lock"); Monitor.Pulse(_lock) //Why is this required when we're about to release the lock anyway? } } static void Count() { lock (_lock) { int count = 0; while(true) { Writeline("Count: " + count++); //give other threads a chance every 10th iteration if (count % 10 == 0) Monitor.Wait(_lock); } } } 

如果我使用Exit()和Enter()而不是Wait(),我可以这样做:

 static object _lock = new Object(); static void Main() { new Thread(Count).Start(); Sleep(10); lock (_lock) Console.WriteLine("Main thread grabbed lock"); } static void Count() { lock (_lock) { int count = 0; while(true) { Writeline("Count: " + count++); //give other threads a chance every 10th iteration if (count % 10 == 0) { Monitor.Exit(_lock); Monitor.Enter(_lock); } } } } 

您使用Enter / Exit获取对锁的独占访问权限。

您使用Wait / Pulse来允许合作通知:我想等待某事发生,所以我进入锁定并呼叫Wait ; 通知代码将进入锁定并调用Pulse

这两个方案是相关的,但他们并没有试图完成同样的事情。

考虑一下你如何实现一个生产者/消费者队列,消费者可以在没有这样的东西的情况下说“当我有一件物品供我消费时叫醒我”。

阅读链接的MSDN页面的“ 备注”部分:

当一个线程调用Wait时,它会释放对象的锁定并进入对象的等待队列。 对象的就绪队列中的下一个线程(如果有)获取锁并且独占使用该对象。 所有调用Wait的线程都会保留在等待队列中,直到它们收到来自Lock的所有者发送的Pulse或PulseAll信号 。 如果发送Pulse,则只有等待队列头部的线程受到影响。 如果发送PulseAll,则所有等待该对象的线程都会受到影响。 当接收到信号时,一个或多个线程离开等待队列并进入就绪队列。 准备队列中的线程被允许重新获取锁。

当调用线程重新获取对象上的锁时,此方法返回。 请注意,如果锁的持有者不调用Pulse或PulseAll,则此方法将无限期地阻塞

因此,基本上,当您调用Monitor.Wait ,您的线程处于等待队列中。 要重新获取锁,它需要处于就绪队列中。 Monitor.Pulse将等待队列中的第一个线程移动到就绪队列,从而允许它重新获取锁。

我自己有同样的疑问,尽管有一些有趣的答案(其中一些在这里出现),我仍然一直在寻找一个更有说服力的答案。

我想在这个问题上有一个有趣而简单的想法是:我可以在特定时刻调用Monitor.Wait(lockObj) ,其中没有其他线程在等待获取lockObj对象的锁。 我只是想等待一些事情发生(例如某个对象的状态要改变),这是我知道最终会在其他一些线程上发生的事情。 一旦达到这个条件,我希望能够在另一个线程释放其锁定时立即重新获取锁定。

通过Monitor.Wait方法的定义,它释放锁并尝试再次获取它。 如果在尝试再次获取锁之前没有等待Monitor.Pulse方法被调用,它只会释放锁并立即再次获取它(取决于您的代码,可能是循环)。

也就是说,我认为通过查看Monitor.Wait方法function的有用性来了解Monitor.Pulse方法的需求是有趣的。

想象一下:“我不想释放这个锁并立即尝试再次获取它,因为我不想成为下一个获取此锁的线程。而且我也不想留在循环包含对Thread.Sleep的调用,检查一些标志或某事,以便知道我正在等待的条件何时已经实现,以便我可以尝试重新获取锁。我只是想’hibernate’并自动唤醒,一旦有人告诉我,我正在等待的条件已经实现。“