Как потреблять BlockingCollection <T> в партиях

Я придумал какой-то код, чтобы потреблять все вещи из очереди. Вместо обработки элементов 1 на 1 имеет смысл обрабатывать все элементы ожидания как набор.

Я объявил свою очередь следующим образом.

private BlockingCollection<Item> items = 
    new BlockingCollection<Item>(new ConcurrentQueue<Item>);

Затем, в потребительском потоке, я планирую читать элементы в партиях, например,

Item nextItem;
while (this.items.TryTake(out nextItem, -1))
{
    var workToDo = new List<Item>();
    workToDo.Add(nextItem);

    while(this.items.TryTake(out nextItem))
    {
        workToDo.Add(nextItem);
    }

    // process workToDo, then go back to the queue.
}

В этом подходе отсутствует полезность GetConsumingEnumerable, и я не могу не задаться вопросом, пропустил ли я лучший способ, или если мой подход ошибочен.

Есть ли лучший способ использовать BlockingCollection<T> в партиях?

Ответ 1

В некотором смысле, не так хорошо, как ConcurrentQueue<T>, мой собственный LLQueue<T> позволяет использовать пакетный dequeue с помощью метода AtomicDequeueAll где все элементы, находящиеся в очереди, берутся из него в одной (атомной и потокобезопасной) операции и затем находятся в коллекции, не связанной с потоками, для потребления одним потоком. Этот метод был разработан именно для сценария, в котором вы хотите выполнять операции чтения.

Это не блокирует, хотя его можно было бы использовать для создания блокирующей коллекции достаточно легко:

public BlockingBatchedQueue<T>
{
  private readonly AutoResetEvent _are = new AutoResetEvent(false);
  private readonly LLQueue<T> _store;
  public void Add(T item)
  {
    _store.Enqueue(item);
    _are.Set();
  }
  public IEnumerable<T> Take()
  {
    _are.WaitOne();
    return _store.AtomicDequeueAll();
  }
  public bool TryTake(out IEnumerable<T> items, int millisecTimeout)
  {
    if(_are.WaitOne(millisecTimeout))
    {
      items = _store.AtomicDequeueAll();
      return true;
    }
    items = null;
    return false;
  }
}

Это отправная точка, которая не выполняет следующие действия:

  • Сдайтесь в ожидании ожидающего читателя после его удаления.
  • Беспокоитесь о потенциальной гонке с несколькими читателями, которые вызваны записью, происходящей во время чтения (она просто считает, что случайный пустой результат перечислим, чтобы быть в порядке).
  • Поместите любую верхнюю границу при записи.

Все это тоже можно добавить, но я хотел бы свести к минимуму какое-то практическое применение, которое, надеюсь, не является ошибкой в ​​определенных ограничениях выше.