C# Don';I don’我不明白监视的必要性
根据,C# Don';I don’我不明白监视的必要性,c#,multithreading,synchronization,C#,Multithreading,Synchronization,根据,Monitor.Wait(): 释放对象上的锁并阻止当前线程,直到 重新获得锁 然而,我读到的关于Wait()和Pulse()的所有内容似乎都表明,仅仅释放另一个线程上的锁是不够的。我需要先调用Pulse()来唤醒等待的线程 我的问题是为什么?线程等待监视器上的锁。Enter()在释放锁时获取它。没有必要“唤醒他们”。它似乎破坏了Wait()的作用 例如 如果使用Exit()和Enter()而不是Wait(),则可以执行以下操作: static object _lock = new Obj
Monitor.Wait()
:
释放对象上的锁并阻止当前线程,直到
重新获得锁
然而,我读到的关于Wait()和Pulse()的所有内容似乎都表明,仅仅释放另一个线程上的锁是不够的。我需要先调用Pulse()来唤醒等待的线程
我的问题是为什么?线程等待监视器上的锁。Enter()在释放锁时获取它。没有必要“唤醒他们”。它似乎破坏了Wait()的作用
例如
如果使用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);
}
}
}
}
阅读链接MSDN页面的备注部分: 当线程调用Wait时,它释放对象上的锁并进入对象的等待队列。对象的就绪队列中的下一个线程(如果有)获取锁并独占使用该对象所有调用Wait的线程都将保持在等待队列中,直到它们收到锁所有者发送的来自Pulse或PULSELL的信号。如果发送脉冲,则仅影响等待队列头部的线程。如果发送PULSELL,则等待对象的所有线程都会受到影响。当接收到信号时,一个或多个线程离开等待队列并进入就绪队列。允许就绪队列中的线程重新获取锁 当调用线程重新获取对象上的锁时,此方法返回请注意,如果锁的保持器不调用Pulse或PULSELL,则此方法将无限期阻止
因此,基本上,当您调用
Monitor.Wait
时,您的线程处于等待队列中。为了重新获取锁,它需要处于就绪队列中Monitor.Pulse
将等待队列中的第一个线程移动到就绪队列,从而允许它重新获取锁。您可以使用输入
/退出
获取对锁的独占访问权
您使用Wait
/Pulse
来允许合作通知:我想等待某些事情发生,所以我进入锁并调用Wait
;通知代码将进入锁并调用脉冲
这两个计划是相关的,但它们并没有试图完成相同的事情
考虑一下如何实现一个生产者/消费者队列,在这个队列中,消费者可以说“当你有一个商品要我消费时叫醒我”,而不需要这样做。我自己也有同样的疑问,尽管有一些有趣的答案(其中一些在这里),我仍然在寻找一个更令人信服的答案 我认为关于这个问题的一个有趣而简单的想法是:我可以在没有其他线程等待获取lockObj对象锁的特定时刻调用Monitor.Wait(lockObj)。我只想等待一些事情发生(例如,某个对象的状态发生变化),我知道这是最终会在其他线程上发生的事情。一旦达到这个条件,我希望能够在另一个线程释放其锁时重新获取锁 根据Monitor.Wait方法的定义,它释放锁并再次尝试获取它。如果它在再次尝试获取锁之前没有等待调用Monitor.Pulse方法,它只需释放锁并立即再次获取它(取决于代码,可能在循环中) 也就是说,我认为通过观察Monitor.Wait方法在功能上的作用来理解Monitor.Pulse方法的需求是很有趣的
这样想:“我不想释放此锁并立即尝试再次获取它,因为我不想成为下一个获取此锁的线程。我也不想停留在一个循环中,循环中包含对线程的调用。Sleep检查一些标志或其他东西,以便知道我等待的条件何时达到,以便我可以尝试重新获取锁。我只想“冬眠”,一旦有人告诉我等待的条件已经达到,我就会自动被唤醒。”。是的,但是为什么要将“等待”和“准备”分开呢“排队?它有什么好处可以弥补我必须拨打额外电话的缺点?+1表示等待队列:另请参阅以获得简单的图形说明。@GazTheDestroyer如果不使用等待队列,则必须进行主动轮询。在许多情况下,这是不可取的,也是无效的。请看生产商和消费者以不同的速度工作。我想我会使用AutoResetEvent。等待/脉冲给了我额外的东西吗?@GazTheDestroyer:IMO,
AutoResetEvent
使用起来更安全。如果不小心执行Wait/Pulse同步,等待线程可能会很容易错过脉冲并永远继续等待。@GazTheDestroyer:就个人而言,我更喜欢Wait/Pulse,我相信在某些情况下它们更有效。与Groo相反,我发现编写没有Wait/Pulse竞争条件的代码更容易,这正是因为Wait和Pulse都必须在已经拥有监视器的线程的上下文中调用。我想这是一个偏好问题。我在几个例子中发现了Wait/Pulse代码中的竞争条件。即使是也有死锁,这表明这个构造是多么微妙。很好地总结了我的观点。@Gazedthedestroyer:你问等待/脉冲给了你什么,而AutoResetEvent没有。它使您能够编写AutoResetEvent,这就是它所做的。像自动重置事件这样的复杂门必须由某些东西构建,如果框架没有恰好提供您个人需要的复杂门的味道,那么如果没有等待和脉冲,您将用哪些部分构建它?
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);
}
}
}
}