Gaz*_*yer 23 c# multithreading synchronization
根据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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果我使用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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Jon*_*eet 35
您使用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.Pulse通过查看其在的运行有效性的方法Monitor.Wait方法.
想象一下:"我不想释放这个锁,并立即尝试再次获取它,因为我不想成为下一个获取此锁的线程.而且我也不想留在循环包含对Thread.Sleep的调用,检查一些标志或某事,以便知道我正在等待的条件何时已经实现,以便我可以尝试重新获取锁.我只是想'休眠'并自动唤醒,一旦有人告诉我,我正在等待的条件已经实现."