Don';我不理解Monitor.Pulse()的必要性

本文关键字:Pulse 必要性 Monitor 不理解 Don | 更新日期: 2023-09-27 18:25:44

根据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);
            }
        }
    }
}

Don';我不理解Monitor.Pulse()的必要性

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

您使用Wait/Pulse来允许协同通知:我想等待发生一些事情,所以我输入锁并调用Wait;则通知码将进入锁并调用CCD_ 7。

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

考虑一下如何实现生产者/消费者队列,在该队列中,消费者可以说"当你有商品要我消费时叫醒我",而不需要这样的话。

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

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

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

也就是说,我认为通过观察Monitor.Wait方法在功能中的有用性来理解Monitor。Pulse的必要性是很有趣的。

这样想:"我不想释放这个锁,然后立即尝试再次获取它,因为我不想成为下一个获取这个锁的线程。我也不想停留在包含对线程的调用的循环中。睡眠检查一些标志或其他东西,以便知道我等待的条件何时达到,这样我就可以尝试重新获取锁。我只想‘休眠当有人告诉我等待的条件已经实现时,nate会自动醒来。".

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

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

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

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