我仍然不确定这两个电话之间的区别。来自 MSDN,
Monitor.Enter(Object)
获取指定对象的排他锁。
Monitor.Wait(Object)
释放对象上的锁并阻塞当前线程,直到它重新获得锁。
由此我假设 Monitor.Wait 与 Monitor.Enter 相同,只是它在重新获取之前首先释放对象上的锁定。
当前线程是否必须首先拥有锁?不同的线程如何强制释放对象的锁?为什么同一个线程要重新获取锁?
我仍然不确定这两个电话之间的区别。来自 MSDN,
Monitor.Enter(Object)
获取指定对象的排他锁。
Monitor.Wait(Object)
释放对象上的锁并阻塞当前线程,直到它重新获得锁。
由此我假设 Monitor.Wait 与 Monitor.Enter 相同,只是它在重新获取之前首先释放对象上的锁定。
当前线程是否必须首先拥有锁?不同的线程如何强制释放对象的锁?为什么同一个线程要重新获取锁?
根据MSDN: Monitor.Wait Method(Object)
SynchronizationLockException:调用线程不拥有指定对象的锁。
换句话说:你只能Monitor.Wait(Object)
在你已经拥有锁时调用,而你调用Monitor.Enter(Object)
是为了获取锁。
至于为什么Monitor.Wait
需要:如果您的线程意识到它缺少继续执行的信息(例如它正在等待信号),您可能希望让其他线程进入临界区,因为并非所有线程都具有相同的先决条件。
要让等待线程继续执行,您需要在释放锁之前Monitor.Pulse(Object)
调用or (否则,您将得到与 with 相同的异常)。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?
}
考虑这个例子:
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)
and的糖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.
}
}
}
我们有什么在这里?
生产者在他喜欢的时候就生产一个对象。一旦他拥有,他就获得队列上的锁,将对象入队,然后进行Pulse
调用。
同时,消费者没有锁,他通过调用离开了它Wait
。一旦他拿到Pulse
那个物体,他就会重新锁定,做他消耗的东西。
所以你在这里得到的是一个直接的线程到线程的通知,告诉消费者有什么事情要做。如果你不这样做,你所能做的就是让消费者继续对集合进行轮询,如果还有事情要做。使用Wait
,您可以确保存在。
正如 Cristi 提到的,幼稚的等待/脉冲代码不起作用。因为您完全错过了这里的关键点:监视器不是消息队列。如果您脉搏而没有人在等待,则脉搏丢失。正确的哲学是你在等待一个条件,如果条件不满足,有办法等待它,不吃cpu,不持有锁。在这里,消费者的条件是队列中有东西。请参阅https://ideone.com/tWqTS1哪个有效(来自 Cristi 示例的一个分支)。
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.
}
}
}