我有一种情况,有时睡眠线程不会被Monitor.PulseAll(object lock)
命令唤醒。这种现象不是确定性的。通常它可以工作,但有时在debbuging期间,睡眠线程无法唤醒,我的队列一直在填满。
如果队列为空,则工作线程进入休眠状态:
private void Dequeue()
{
try
{
while (true)
{
T item;
lock (_lock)
{
if (_queue.Count == 0)
{
Monitor.Wait(_lock); //thread stays asleep here. why?
}
item = _queue.Dequeue(); //break point [1]
}
}
}
catch (ThreadAbortException ex)
{
_logger.Error(ex)
}
}
添加队列时,至少有一个项目,锁定对象上的PulseAll
应该唤醒线程。
public void Add(T item)
{
Validate.NotNull(item, "item must not be null");
lock (_lock)
{
_queue.Enqueue(item);
_queueInfoAdministrator.IncrementCount();
Monitor.PulseAll(_lock);
}
}
是否有其他人有类似的经历或者可以指出我正确的方向,为什么会发生这种情况(有时)?
编辑2011.04.08:
更多信息 - 只有一个消费者线程。所以理论上Pulse
就足够了。一旦达到状态,消费者线程保持睡眠状态,我可以继续将项目排队,然后调用PulseAll
而不能唤醒睡眠线程。我放置了一个断点[1] ,它在所描述的情况下永远不会被击中。因此,我认为它不是像MSDN页面中描述的脉冲/监视器的死锁问题。
答案 0 :(得分:1)
当Dequeue功能没有等待时,你是否正在发出脉冲?如果是这样,则在下一个脉冲之前不会触发等待。
请参阅Pulse
上的重要说明答案 1 :(得分:1)
你确定线程是睡着的吗?您的代码调用_queue.Dequeue
,如果队列中没有项目,则会抛出异常。因此,如果两个线程正在等待锁定,那么当Add
方法将一个项目排入队列并调用PulseAll
时,两个线程都将被释放。第一个线程将调用Dequeue
并获取队列中的唯一项。下一个线程将调用Dequeue
并抛出异常。如果你正在捕捉并吞下这种异常,你将永远不会看到它。由于异常已经转义while
循环,因此线程终止并且队列将开始填充。
此外,虽然我怀疑这是您的问题,但请参阅http://msdn.microsoft.com/en-us/library/system.threading.monitor.pulse.aspx,其中使用Pulse
和PulseAll
讨论潜在的死锁问题。
答案 2 :(得分:0)
Wait
调用应始终在等待条件的While
循环中使用。
原始代码不使用While
循环,但使用If语句。
lock (_lock)
{
// wrong: if (_queue.Count == 0)
while (_queue.Count == 0)
{
Monitor.Wait(_lock); //thread stays asleep here. why?
}
有关详细说明,请参阅"使用等待和脉冲信号"本文第4部分:Threading in C#