Сценарий ограниченной очереди - PullRequest
1 голос
/ 31 января 2010

Мне нужно реализовать очередь, ограниченную производителем / потребителем, несколько потребителей против одного производителя.

У меня есть функция push, которая добавляет элемент в очередь, а затем проверяет максимальный размер. Если мы достигли его, верните false, в любом другом случае верните true.

В следующем коде _vector является списком , onSignal в основном использует элемент асинхронным способом.

Вы видите проблемы с этим кодом?

public bool Push(T message)
{
    bool canEnqueue = true;

    lock (_vector)
    {
        _vector.Add(message);
        if (_vector.Count >= _maxSize)
        {
            canEnqueue = false;
        }
    }

    var onSignal = SignalEvent;
    if (onSignal != null)
    {
        onSignal();
    }

    return canEnqueue;
}

Ответы [ 2 ]

1 голос
/ 31 января 2010

Самая большая проблема, которую я вижу, это использование List<T> для реализации очереди;при этом возникают проблемы с производительностью, поскольку удаление первого элемента подразумевает копирование всех данных.

Дополнительные мысли;Вы повышаете сигнал, даже если вы не добавили данные, и использование событий само может иметь проблемы с многопоточностью (есть некоторые крайние случаи, даже когда вы захватываетезначение до теста null - плюс это возможно * на 1010 * больше служебных данных, чем при использовании Monitor для передачи сигналов).

Я бы переключился на Queue<T>, который не будетиметь эту проблему - или лучше использовать предварительно свернутый пример;например Создание очереди блокировки в .NET? , которая делает именно то, что вы обсуждаете, и поддерживает любое количество как производителей, так и потребителей.Он использует блокирующий подход, но подход «try» будет следующим:

public bool TryEnqueue(T item)
{
    lock (queue)
    {
        if (queue.Count >= maxSize) { return false; }
        queue.Enqueue(item);
        if (queue.Count == 1)
        {
            // wake up any blocked dequeue
            Monitor.PulseAll(queue);
        }
        return true;
    }
}

Наконец - вы не «проталкиваете» стек , а не очередь?

1 голос
/ 31 января 2010

Я знаю, что вы сказали, что один производитель, несколько потребителей, но все равно стоит упомянуть: если ваша очередь почти заполнена (скажем, 24 из 25 слотов), то, если два потока Push одновременно, вы будете в конечном итоге превышение лимита. Если даже есть вероятность, что в какой-то момент в будущем у вас может появиться несколько производителей, вам следует подумать о том, чтобы сделать Push блокирующим вызовом, и попросить его дождаться «доступного» * ​​1003 *, который сигнализируется после того, как элемент снят с производства или после того, как предмет помещен в очередь, пока есть свободные места.

Единственная другая потенциальная проблема, которую я вижу, это SignalEvent. Вы не показываете нам реализацию этого. Если он объявлен как public event SignalEventDelegate SignalEvent, то все будет в порядке, потому что компилятор автоматически добавляет SynchronizedAttribute. Однако, если SignalEvent использует делегата поддержки с синтаксисом add / remove, то вам нужно будет обеспечить собственную блокировку для самого события, в противном случае потребитель сможет немного отсоединиться от события. слишком поздно и все еще получаю пару сигналов потом.

Редактировать: На самом деле это возможно независимо; что еще более важно, если вы использовали делегат в стиле свойств для добавления / удаления без соответствующей блокировки, то при попытке его выполнения делегат может оказаться в недопустимом состоянии. Даже с синхронизированным событием потребители должны быть готовы получать (и отменять) уведомления после того, как они отписались.

Кроме этого, я не вижу проблем - хотя это не значит, что их нет, просто я не заметил никаких.

...