Monitor.Enter vs Monitor.Wait

时间:2013-06-05 14:25:38

标签: c# multithreading locking

我仍然不确定这两个电话之间的区别。来自MSDN,

Monitor.Enter(Object)获取指定对象的独占锁。

Monitor.Wait(Object)释放对象的锁定并阻止当前线程,直到它重新获取锁定。

由此我假设Monitor.Wait与Monitor.Enter相同,只是它在重新获取之前首先释放对象的锁定。

当前线程是否必须首先拥有锁定?一个不同的线程如何强制释放对象的锁定?为什么同一个线程想要重新获取锁?

3 个答案:

答案 0 :(得分:7)

根据MSDN: Monitor.Wait Method(Object)

  

SynchronizationLockException:调用线程不拥有指定对象的锁。

换句话说:当您已拥有锁定时,您只能致电Monitor.Wait(Object),而为了获取而致电Monitor.Enter(Object)锁。

至于为什么需要Monitor.Wait:如果你的线程意识到它缺乏继续执行的信息(例如它正在等待信号),你可能想让其他线程进入临界区,因为不是所有线程都有相同的先决条件。

要等待线程继续执行,您需要在释放锁定之前致电Monitor.Pulse(Object)Monitor.PulseAll(Object) (否则,您将获得相同类型与Monitor.Wait(Object))一样的例外情况。

请记住,在脉冲发生后和锁定释放后获取锁定的下一个线程不一定是接收脉冲的线程。

另外请记住,接受脉搏并不等同于满足您的病情。您可能仍需要等待一段时间:

// make sure to synchronize this correctly ;)
while (ConditionNotMet)
{
    Monitor.Wait(mutex);
    if (ConditionNotMet) // We woke up, but our condition is still not met
        Monitor.Pulse(mutex); // Perhaps another waiting thread wants to wake up?
}

答案 1 :(得分:1)

考虑这个例子:

public class EnterExitExample
{
    private object myLock;
    private bool running;

    private void ThreadProc1()
    {
        while (running)
        {
            lock (myLock)
            {
                // Do stuff here...
            }
            Thread.Yield();
        }
    }

    private void ThreadProc2()
    {
        while (running)
        {
            lock (myLock)
            {
                // Do other stuff here...
            }
            Thread.Yield();
        }
    }
}

现在你有两个线程,每个线程等待锁定,然后执行它们的操作,然后释放锁定。 lock (myLock)语法仅适用于Monitor.Enter(myLock)Monitor.Exit(myLock)

现在让我们看一个更复杂的例子,其中WaitPulse发挥作用。

public class PulseWaitExample
{
    private Queue<object> queue;
    private bool running;

    private void ProducerThreadProc()
    {
        while (running)
        {
            object produced = ...; // Do production stuff here.
            lock (queue)
            {
                queue.Enqueue(produced);
                Monitor.Pulse(queue);
            }
        }
    }

    private void ConsumerThreadProc()
    {
        while (running)
        {
            object toBeConsumed;
            lock (queue)
            {
                Monitor.Wait(queue);
                toBeConsumed = queue.Dequeue();
            }
            // Do consuming stuff with toBeConsumed here.
        }
    }
}

我们在这里有什么?

制作人在他感觉到的时候会产生一个物体。他一旦获得,就获得了对队列的锁定,将对象排入队列,然后进行Pulse调用。

与此同时,消费者没有锁定,他通过致电Wait离开了。一旦他在该物体上获得Pulse,他就会重新锁定并做他消耗的东西。

所以你在这里有一个直接的线程到线程的通知,有消费者需要做的事情。如果您没有这个,那么您所能做的就是让消费者继续轮询该集合,如果还有什么可做的话。使用Wait,您可以确保有。{/ p>

答案 2 :(得分:0)

正如Cristi所说,天真的等待/脉冲代码不起作用。因为您完全忽略了关键点:监视器不是消息队列。如果你是脉冲并且没有人在等待,则脉冲是LOST。 正确的理念是你正在等待一个条件,如果条件不满意,有一种方法可以等待它,而不需要吃cpu而不需要锁定。这里,消费者的条件是队列中有东西。 请参阅https://ideone.com/tWqTS1哪项工作(来自Cristi&#39的示例)。

public class PulseWaitExample
{
    private Queue<object> queue;
    private bool running;

    private void ProducerThreadProc()
    {
        while (running)
        {
            object produced = ...; // Do production stuff here.
            lock (queue)
            {
                queue.Enqueue(produced);
                Monitor.Pulse(queue);
            }
        }
    }

    private void ConsumerThreadProc()
    {
        while (running)
        {
            object toBeConsumed;
            lock (queue)
            {
                // here is the fix
                if (queue.Count == 0)
                { 
                   Monitor.Wait(queue);
                }
                toBeConsumed = queue.Dequeue();
            }
            // Do consuming stuff with toBeConsumed here.
        }
    }
}