Ответ 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;
}
}
Это отправная точка, которая не выполняет следующие действия:
- Сдайтесь в ожидании ожидающего читателя после его удаления.
- Беспокоитесь о потенциальной гонке с несколькими читателями, которые вызваны записью, происходящей во время чтения (она просто считает, что случайный пустой результат перечислим, чтобы быть в порядке).
- Поместите любую верхнюю границу при записи.
Все это тоже можно добавить, но я хотел бы свести к минимуму какое-то практическое применение, которое, надеюсь, не является ошибкой в определенных ограничениях выше.