BlockingCollection<>中的元素顺序

14

我有一个使用BlockingCollection<>实现的下载队列。现在我想偶尔优先处理一些下载。我考虑过可能会像列表中一样将某些元素“上移”,但是没有像Remove() / AddFirst()或Move()这样的方法。

BlockingCollection<>中排列项目的首选方法是什么?


相关:具有优先级的并发集合 - Theodor Zoulias
4个回答

18

很遗憾,没有办法以您所期望的方式重新排列队列。您真正需要的是一个作为优先队列实现的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
BlockingCollection<T>通过包装一个内部的IProducerConsumerCollection<T>实现。默认情况下,它会在内部使用ConcurrentQueue<T>,但你可以通过这个构造函数提供自己的实现。
如果你提供了自己的线程安全集合,你可以使用任何类型的集合。这将允许你按需优先处理元素。
虽然没有内置的集合可以实现你想要的功能,但你可以将一对ConcurrentQueue<T>集合封装到一个实现IProducerConsumerCollection<T>的类中。这将允许你拥有“高优先级”和“低优先级”的元素。

5
Reed在告诉你需要实现,然而有一个可以帮助你的类。它不是内置的,但在MSDN上有介绍。只需将此传递给您的。

这是我使用它的方式:

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;
}

5

无法直接在BlockingCollection<T>上实现优先级队列。一个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中实际存在的类型。它是基于下载项目的优先级调度,需要自己编写的内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接