代码之家  ›  专栏  ›  技术社区  ›  Rob

这个阻塞队列易受死锁影响吗?

  •  3
  • Rob  · 技术社区  · 14 年前

    我一直把这段代码当作一个队列 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 回复  |  直到 14 年前
        1
  •  7
  •   Dan Tao    14 年前

    我想这可能是你的问题:

    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)
    
        2
  •  1
  •   Marcelo Cantos    14 年前

    这段代码在几个方面被破坏了。这里有一个场景。有一个种族状况 if (Size == 0) _event.Reset() . 两个队列之间可能会发生排队,其信号将丢失。

    使用信号量更容易实现无边界长度的阻塞队列。

        3
  •  0
  •   Chris Schmich    14 年前

    我不知道你的需求或者你的类还做什么,但是如果你能使用.net 4,你可能会考虑使用 ConcurrentQueue<T> BlockingCollection<T> 一起使用,应该会给你一个阻塞队列。