我有一个使用BlockingCollection<>
实施的下载队列。现在我想暂时优先考虑一些下载。我认为将一些元素“向上”移动可能会很棒,就像在列表中一样,但是没有像Remove()/ AddFirst()或Move()这样的方法。
在BlockingCollection<>
中安排项目的首选方法是什么?
答案 0 :(得分:17)
不幸的是,没有办法以你想要的方式重新排列队列。你真正需要的是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
它强制您使用多个队列,因此它不是最优雅的解决方案。
答案 1 :(得分:8)
BlockingCollection<T>
通过包装内部IProducerConsumerCollection<T>
来发挥作用。默认设置是在内部使用ConcurrentQueue<T>
,但您可以通过this constructor提供自己的实施。
如果您提供自己的线程安全集合,则可以使用所需的任何集合类型。这将允许您根据需要确定元素的优先级。
虽然没有可实现所需功能的内置集合,但您可以将一对ConcurrentQueue<T>
集合包装到实现IProducerConsumerCollection<T>
的类中。这将允许您拥有“高优先级”和“低优先级”元素。
答案 2 :(得分: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中实际存在的类型。根据下载项目的优先级安排,您需要自己编写。
答案 3 :(得分:3)
Reed告诉您需要实施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;
}