Это BlockingQueue подвержено тупику? - PullRequest
3 голосов
/ 07 мая 2010

Я использовал этот код как очередь, которая блокируется на 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 ]

7 голосов
/ 07 мая 2010

Я думаю, что это может быть вашей проблемой:

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 голос
/ 07 мая 2010

Этот код нарушается несколькими способами. Вот один сценарий. Есть расы между if (Size == 0) и _event.Reset(). Enqueue может сработать между двумя, и его сигнал будет потерян.

BlockingQueue неограниченной длины гораздо проще реализовать с помощью семафора.

0 голосов
/ 08 мая 2010

Я не знаю о ваших требованиях или о том, что еще делает ваш класс, но если вы можете использовать .NET 4, вы можете рассмотреть возможность использования ConcurrentQueue<T> и BlockingCollection<T>, который при совместном использовании должен дать вам очередь блокировки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...