这个BlockingQueue容易出现死锁吗?

时间:2010-05-07 13:15:24

标签: c# .net multithreading deadlock

我一直在使用此代码作为阻塞Dequeue()的队列,直到一个元素入队。我已经在几个项目中使用了这段代码几年,所有项目都没有问题......直到现在。我现在正在编写一些代码中的僵局,在调查问题时,我的“怀疑之眼”已经解决了BlockingQueue<T>。我无法证明这一点,所以我想我会问一些比我聪明的人来检查潜在的问题。你能在这段代码中看到任何可能导致死锁的事吗?

public class BlockingQueue<T>
{
    private readonly Queue<T> _queue;
    private readonly ManualResetEvent _event;

    /// <summary>
    /// Constructor
    /// </summary>
    public BlockingQueue()
    {
        _queue = new Queue<T>();
        _event = new ManualResetEvent(false);
    }

    /// <summary>
    /// Read-only property to get the size of the queue
    /// </summary>
    public int Size
    {
        get
        {
            int count;

            lock (_queue)
            {
                count = _queue.Count;
            }

            return count;
        }
    }

    /// <summary>
    /// Enqueues element on the queue
    /// </summary>
    /// <param name="element">Element to enqueue</param>
    public void Enqueue(T element)
    {
        lock (_queue)
        {
            _queue.Enqueue(element);
            _event.Set();
        }
    }

    /// <summary>
    /// Dequeues an element from the queue
    /// </summary>
    /// <returns>Dequeued element</returns>
    public T Dequeue()
    {
        T element;

        while (true)
        {
            if (Size == 0)
            {
                _event.Reset();
                _event.WaitOne();
            }

            lock (_queue)
            {
                if (_queue.Count == 0) continue;

                element = _queue.Dequeue();
                break;
            }
        }

        return element;
    }

    /// <summary>
    /// Clears the queue
    /// </summary>
    public void Clear()
    {
        lock (_queue)
        {
            _queue.Clear();
        }
    }
}

3 个答案:

答案 0 :(得分:7)

我认为这可能是你的问题:

Thread 1                    Thread 2
Dequeue
                            Enqueue    
if (Size == 0)                              // Thread 1 gets the lock
                            lock (_queue)   // Thread 2 has to wait
return _queue.Count                         // Thread 1 sees: Size == 0
                            _queue.Enqueue  // Thread 2 gets the lock
                            _event.Set    
_event.Reset                                // uh oh
_event.WaitOne                              // now Dequeue's going to block
                                            // until Enqueue gets called again
                                            // (even though queue isn't empty)

答案 1 :(得分:1)

此代码以多种方式破解。这是一个场景。 if (Size == 0)_event.Reset()之间存在竞争条件。 Enqueue可能在两者之间发射,其信号将丢失。

使用信号量可以更轻松地实现无限长的BlockingQueue。

答案 2 :(得分:0)

我不了解您的要求或您的课程的其他内容,但如果您可以使用.NET 4,您可能需要考虑使用ConcurrentQueue<T>BlockingCollection<T>,它们一起使用,应该给你一个阻塞队列。