C#PulseAll不会唤醒睡眠线程

时间:2011-04-04 14:53:10

标签: c# multithreading

我有一种情况,有时睡眠线程不会被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页面中描述的脉冲/监视器的死锁问题。

3 个答案:

答案 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,其中使用PulsePulseAll讨论潜在的死锁问题。

答案 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#