Порядок элементов в BlockingCollection <> - PullRequest
12 голосов
/ 21 сентября 2011

У меня есть очередь загрузки, реализованная с BlockingCollection<>.Теперь я хочу расставить приоритеты для некоторых загрузок время от времени.Я подумал, что было бы замечательно переместить некоторые элементы «вверх» в Коллекцию, как в списке, но нет метода, такого как Remove () / AddFirst () или Move ().расстановка предметов в BlockingCollection<>?

Ответы [ 4 ]

17 голосов
/ 21 сентября 2011

К сожалению, нет способа переставить очередь так, как вы хотите.Что вам действительно нужно, так это PriorityBlockingCollection, реализованный в виде очереди с приоритетами, но, увы, тоже не существует.

Что вы можете сделать, так это воспользоваться методом TakeFromAny, чтобы получить желаемое поведение приоритета.TakeFromAny удалит первый доступный элемент из массива BlockingCollection экземпляров.Он будет отдавать приоритет очередям, перечисленным первым в массиве.

var low = new BlockingCollection<object> { "low1", "low2" };
var high = new BlockingCollection<object> { "high1", "high2" };
var array = new BlockingCollection<object>[] { high, low };
while (true)
{
  object item;
  int index = BlockingCollection<object>.TakeFromAny(array, out item);
  Console.WriteLine(item);
}

В приведенном выше примере будет напечатано:

high1
high2
low1
low2

Он заставляет вас использовать несколько очередей, поэтому он не самыйэлегантное решение.

8 голосов
/ 21 сентября 2011

BlockingCollection<T> работает, оборачивая внутренний IProducerConsumerCollection<T>.По умолчанию используется ConcurrentQueue<T> для внутреннего использования, но вы можете предоставить собственную реализацию через этот конструктор .

Если вы предоставите свою собственную потокобезопасную коллекцию, вы можете использовать любой тип коллекции, какой захотите,Это позволит вам расставлять приоритеты для элементов по мере необходимости.

Хотя нет встроенных коллекций, которые бы реализовывали желаемую функциональность, вы, вероятно, могли бы обернуть пару ConcurrentQueue<T> коллекций в класс, который реализует IProducerConsumerCollection<T>,Это позволит вам иметь элементы с «высоким приоритетом» и «низким приоритетом».

5 голосов
/ 21 сентября 2011

Невозможно реализовать очередь с приоритетами непосредственно поверх BlockingCollection<T>. A BlockingCollection<T> лучше всего рассматривать как строгую очередь, для которой невозможно выполнить переупорядочение.

Однако вы можете использовать комбинацию очереди приоритетов и BlockingCollection<T> для достижения того же эффекта. Предположим, на секунду вы внедрили простой PriorityQueue<T>, который корректно упорядочивает ваши загрузки. Следующее может быть использовано для добавления приоритета к обработке принимающей стороны

class DownloadManager {
  private PriorityQueue<Download> m_priorityQueue;
  private BlockingCollection<Download> m_downloadCollection;

  public bool TryGetNext(ref Download download) {
    PumpDownloadCollection();
    if (m_priorityQueue.IsEmpty) {
      download = null;
      return false;
    }

    download = m_priorityQueue.Dequeue();
    return true;
  }

  private void PumpDownloadCollection() {
    T value;
    while (m_downloadCollection.TryTake(out value)) {
      m_priorityQueue.Enqueue(value);
    }
  }

Примечание: PriorityQueue<T> - это не тот тип, который действительно существует в .Net Framework. Это то, что вам нужно написать самостоятельно, основываясь на приоритетном планировании загружаемых элементов.

3 голосов
/ 30 октября 2015

Рид правильно сказал, что вам нужно реализовать IProducerConsumerCollection<T>.Тем не менее, есть класс, который может вам помочь.Он не встроен, но установлен на MSDN .Просто передайте это ConcurrentPriorityQueue вашему BlockingCollection.

Вот как я его использовал:

private readonly BlockingCollection<KeyValuePair<int, ICommand>> _commands 
    = new BlockingCollection<KeyValuePair<int, ICommand>>(
        new ConcurrentPriorityQueue<int, ICommand>());

ICommand - это интерфейс в моем проекте.

Теперь это позволяет добавлять такие элементы:

_actions.Add(new KeyValuePair<int, ICommand>(1, command1));
_actions.Add(new KeyValuePair<int, ICommand>(2, command2));
_actions.Add(new KeyValuePair<int, ICommand>(1, command3));

Элементы с более низким целочисленным значением в качестве приоритета будут выполняться первыми.В приведенном выше примере:

command1
command3
command2

При циклическом цикле над BlockingCollection вы больше не получите одиночные элементы (ICommand в моем случае), а KeyValuePair.Это может потребовать некоторых изменений кода, конечно.Приятно, что у вас есть первоначальный приоритет:

foreach (var command in _queue) 
{
    var priority = command.Key;
    var actualCommand = command.Value;
}
...