C# 监视器。输入vs监视器。等待

C# 监视器。输入vs监视器。等待,c#,multithreading,locking,C#,Multithreading,Locking,我仍然不确定这两个电话的区别。来自MSDN Monitor.Enter(Object)获取指定对象的独占锁 Monitor.Wait(Object)释放对象上的锁并阻止当前线程,直到它重新获得锁为止 由此我假设Monitor.Wait与Monitor.Enter相同,只是它在重新获取之前首先释放对象上的锁 当前线程必须首先拥有锁吗?不同的线程如何强制释放对象的锁?为什么同一线程要重新获取锁?根据 SynchronizationLockException:调用线程不拥有指定对象的锁 换句话说:当您

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

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

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

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

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

根据

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

换句话说:当您已经拥有锁时,您只能调用
Monitor.Wait(Object)
,而调用
Monitor.Enter(Object)
获取锁

至于为什么需要
Monitor.Wait
:如果您的线程意识到它缺少继续执行的信息(例如,它正在等待信号),您可能希望让其他线程进入关键部分,因为并非所有线程都具有相同的先决条件

要使等待线程继续执行,您需要在释放锁之前调用
Monitor.Pulse(Object)
Monitor.pulsell(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?
}
考虑这个例子:

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)

现在让我们看一个更复杂的例子,
Wait
Pulse
起作用

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.
        }
    }
}
公共类pulswait示例
{
专用队列;
私奔;
私有无效生产商ReadProc()
{
(跑步时)
{
生成的对象=…;//在此处执行生成操作。
锁(队列)
{
排队。排队(生成);
监测脉冲(队列);
}
}
}
私有void ConsumerThreadProc()
{
(跑步时)
{
消费对象;
锁(队列)
{
监视器。等待(队列);
toBeConsumed=queue.Dequeue();
}
//在这里消费的东西。
}
}
}
我们这里有什么

制作人只要想就制作一件物品。一旦有,他获得队列上的锁,让对象排队,然后执行
脉冲
调用

同时,消费者没有锁,他通过调用
Wait
离开了锁。一旦他在那个物体上得到一个
脉冲
,他就会重新锁定,然后做他的消费工作


因此,这里有一个直接的线程到线程的通知,表示有一些事情要为消费者做。如果你不想这样做,你所能做的就是让消费者在有事情要做的时候继续对收集进行投票。使用
等待
,您可以确保有。

正如克里斯蒂所提到的,简单的等待/脉冲代码是不起作用的。因为您完全忽略了这里的关键点:监视器不是消息队列。如果你脉搏跳动,没有人在等待,脉搏就会消失。 正确的理念是,您正在等待一个条件,如果条件不满足,有一种方法可以等待它,而无需占用cpu,也无需持有锁。这里,消费者的条件是队列中有东西。 看哪一个作品(克里斯蒂的例子中的叉子)

公共类pulswait示例
{
专用队列;
私奔;
私有无效生产商ReadProc()
{
(跑步时)
{
生成的对象=…;//在此处执行生成操作。
锁(队列)
{
排队。排队(生成);
监测脉冲(队列);
}
}
}
私有void ConsumerThreadProc()
{
(跑步时)
{
消费对象;
锁(队列)
{
//这是解决办法
如果(queue.Count==0)
{ 
监视器。等待(队列);
}
toBeConsumed=queue.Dequeue();
}
//在这里消费的东西。
}
}
}

Enter/Exit用于互斥,Wait/Pulse(All)用于等待和发信号。从另一个预期答案中可以看到“当您已经拥有锁时,您只能调用Monitor.Wait(对象)”。当生产者拥有锁时,消费者可以呼叫Monitor.Wait(queue)吗?生产者拥有锁的时间很短,即
队列.Enqueue
Monitor.Pulse
调用所需的时间,后者会放弃锁。消费者要么完全在锁块之外,这意味着他将刚刚获得锁,呼叫
等待
,由于持续的
脉冲
,立即停止等待,要么他在
等待
呼叫内,让他重新获取锁并进行消费。根据上的文档,这看起来像是一个等待发生的死锁:“Monitor类不维护指示已调用Pulse方法的状态。因此,如果在n
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.
        }
    }
}