BlockingCollection<> 中的元素顺序

发布于 2024-12-05 21:56:18 字数 211 浏览 4 评论 0 原文

我有一个使用 BlockingCollection<> 实现的下载队列。现在我想偶尔优先进行一些下载。我认为将一些元素“向上”移动到集合中可能很棒,就像在列表中一样,但是没有像Remove()/AddFirst()或Move()这样的方法。

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

I have a Download Queue implemented with BlockingCollection<>. Now I want to prioritize some Download once in a while. I thought it might be great to move some elements 'up' the Collection, like in a list, but there is no method like Remove()/AddFirst() or Move().

What's the preferred way of arranging items in a BlockingCollection<> ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

养猫人 2024-12-12 21:56: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

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

Unfortunately there is no way to rearrange the queue in the manner you desire. What you really need is a PriorityBlockingCollection implemented as a priority queue, but alas that does not exist either.

What you can do is exploit the TakeFromAny method to get the priority behavior you want. TakeFromAny will dequeue the first available item from an array of BlockingCollection instances. It will give priority to queues listed first in the array.

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

The example above will print:

high1
high2
low1
low2

It forces you to use to multiple queues so it is not the most elegant solution.

爱她像谁 2024-12-12 21:56:18

BlockingCollection 通过包装内部 来工作IProducerConsumerCollection。默认情况下在内部使用 ConcurrentQueue,但您可以通过 此构造函数

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

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

BlockingCollection<T> works by wrapping an internal IProducerConsumerCollection<T>. The default is to use a ConcurrentQueue<T> internally, but you can provide your own implementation via this constructor.

If you provide your own threadsafe collection, you can use any collection type you want. This would allow to you prioritize elements as needed.

While there are no built-in collections that will implement your desired functionality, you could probably wrap a pair of ConcurrentQueue<T> collections into a class that implements IProducerConsumerCollection<T>. This would allow you to have "high priority" and "low priority" elements.

不气馁 2024-12-12 21:56:18

无法直接在 BlockingCollection 之上实现优先级队列。最好将 BlockingCollection 视为无法实现重新排序的严格队列。

不过,您可以结合使用优先级队列和 BlockingCollection 来实现相同的效果。让我们假设您实现了一个简单的 PriorityQueue,它可以正确排序您的下载。以下内容可用于为接收方的处理添加优先级

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

There is no way to implement a priority queue directly on top of a BlockingCollection<T>. A BlockingCollection<T> is best viewed as a strict queue for which no reordering can be achieved.

However you could use a combination of a priority queue and a BlockingCollection<T> to achieve the same effect. Lets assume for a second you implemented a simple PriorityQueue<T> which correctly orders your downloads. The following could be used to add priority to the handling of the receiving side

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

Note: PriorityQueue<T> is not a type that actually exists in the .Net Framework. It's something you'd need to write yourself based on the priority scheduling of downloaded items.

撑一把青伞 2024-12-12 21:56:18

Reed 正确地告诉您需要实现 IProducerConsumerCollection。但是,有一门课程可以帮助您。它不是内置的,但它在 上有特色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;
}

Reed is correct in telling you that you need to implement the IProducerConsumerCollection<T>. However, there is a class that can help you. It's not built in, but it's featured on MSDN. Just pass this ConcurrentPriorityQueue to your BlockingCollection.

This is how I used it:

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

The ICommand is an interface in my project.

Now this allows you to add items like this:

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

Items with a lower integer value as priority will be executed first. In the above example:

command1
command3
command2

When looping over your BlockingCollection, you will no longer get the single elements (ICommand in my case), but a KeyValuePair. This might require some code changes of course. A nice thing is that you have its original priority:

foreach (var command in _queue) 
{
    var priority = command.Key;
    var actualCommand = command.Value;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文