Ограниченный сценарий очереди
-
24-09-2019 - |
Вопрос
Мне нужно реализовать допущенную очередью производителя / потребителя, несколько потребителей против одного производителя.
У меня есть функция push, которая добавляет элемент в очередь, а затем проверяет maxsize. Если мы достигли его, верните FALSE, в любом другом случае вернитесь.
В следующем коде _Vector - это списокu003CT> 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;
}
Решение
Я знаю, что вы сказали, что однопроизводитель, многократный потребитель, но все равно стоит упомянуть: если ваша очередь почти полна (скажем, 24 из 25 слотов), то если два потока Push
В то же время вы в конечном итоге превышаете предел. Если есть даже шанс, у вас может быть несколько производителей в какой-то момент в будущем, вы должны рассмотреть Push
Блокирующий вызов, и у него дождаться «доступное» AutoResetEvent
который сигнализируется после удаления элемента, либо после того, как элемент трансформируется, когда доступны еще слоты.
Единственная другая потенциальная проблема, которую я вижу, это SignalEvent
. Отказ Вы не показываете нам реализацию этого. Если он объявлен как public event SignalEventDelegate SignalEvent
, тогда вы будете в порядке, потому что компилятор автоматически добавляет SynchronizedAttribute
. Отказ Однако, если SignalEvent
использует делегат поддержки с add
/remove
Синтаксис, то вам нужно будет предоставить свою собственную блокировку для самого мероприятия, в противном случае будет возможно для потребителя от события чуть немного поздно и по-прежнему получить пару сигналов позже.
Редактировать: на самом деле, это возможно независимо; Что еще более важно, если вы использовали делегат «Добавить / удалить стиль свойств без соответствующего блокировки», на самом деле возможно для делегата в неверном состоянии, когда вы пытаетесь выполнить его. Даже с синхронизированным событием потребителей должны быть готовы получать (и отбросить) уведомления после того, как они отказываются отписались.
Кроме того, я не вижу проблем - хотя это не значит, что нет, это просто означает, что я не заметил никого.
Другие советы
Самая большая проблема, которую я вижу, есть использование List<T>
реализовать очередь; Существуют проблемы с производительностью, поскольку удаление первого элемента включает копирование всех данных.
Дополнительные мысли; Вы поднимаете сигнал, даже если вы не Добавить данные и использование событий сам может иметь проблему с резьбой (есть несколько краевых чехлов, даже если вы зарабатываете значение до null
Тест - плюс это возможно больше накладных расходов, чем использование Monitor
сделать сигнализацию).
Я бы перешел на Queue<T>
что не будет иметь эту проблему - или лучше использовать предварительно прокатный пример; Например Создание блокирующей очереди в .NET?, который имеет именно то, что вы обсуждаете, и поддерживает любое количество как производителей, так и потребителей. Он использует блокирующий подход, но «попробуйте» подход будет:
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;
}
}
Наконец - не «нажимаешь» на куча, не очередь?