【问题标题】:Element order in BlockingCollection<>BlockingCollection<> 中的元素顺序
【发布时间】:2011-11-22 02:09:04
【问题描述】:

我有一个使用BlockingCollection&lt;&gt; 实现的下载队列。现在我想偶尔优先考虑一些下载。我认为将某些元素“向上”移动到集合中可能会很棒,例如在列表中,但是没有像 Remove()/AddFirst() 或 Move() 这样的方法。

BlockingCollection&lt;&gt; 中排列项目的首选方式是什么?

【问题讨论】:

    标签: .net multithreading collections concurrency queue


    【解决方案1】:

    Reed 告诉您需要实现IProducerConsumerCollection&lt;T&gt; 是正确的。但是,有一个课程可以帮助您。它不是内置的,但它在 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;
    }
    

    【讨论】:

      【解决方案2】:

      很遗憾,无法按照您想要的方式重新排列队列。您真正需要的是作为优先级队列实现的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
      

      它迫使您使用多个队列,因此它不是最优雅的解决方案。

      【讨论】:

        【解决方案3】:

        没有办法直接在BlockingCollection&lt;T&gt; 之上实现优先级队列。 BlockingCollection&lt;T&gt; 最好被视为一个严格的队列,不能对其进行重新排序。

        但是,您可以结合使用优先级队列和BlockingCollection&lt;T&gt; 来实现相同的效果。让我们假设您实现了一个简单的PriorityQueue&lt;T&gt;,它正确地订购了您的下载。以下可用于增加接收方处理的优先级

        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&lt;T&gt; 不是 .Net Framework 中实际存在的类型。这是您需要根据下载项目的优先级安排自己编写的内容。

        【讨论】:

          【解决方案4】:

          BlockingCollection&lt;T&gt; 通过包装内部IProducerConsumerCollection&lt;T&gt; 工作。默认是在内部使用ConcurrentQueue&lt;T&gt;,但您可以通过this constructor 提供自己的实现。

          如果您提供自己的线程安全集合,您可以使用任何您想要的集合类型。这将允许您根据需要对元素进行优先级排序。

          虽然没有可以实现所需功能的内置集合,但您可以将一对ConcurrentQueue&lt;T&gt; 集合包装到实现IProducerConsumerCollection&lt;T&gt; 的类中。这将允许您拥有“高优先级”和“低优先级”元素。

          【讨论】:

            猜你喜欢
            • 2021-12-06
            • 1970-01-01
            • 2011-04-19
            • 2011-02-07
            • 1970-01-01
            • 2011-04-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多