我有一个使用BlockingCollection<>
实现的下载队列。现在我想偶尔优先处理一些下载。我考虑过可能会像列表中一样将某些元素“上移”,但是没有像Remove() / AddFirst()或Move()这样的方法。
在BlockingCollection<>
中排列项目的首选方法是什么?
我有一个使用BlockingCollection<>
实现的下载队列。现在我想偶尔优先处理一些下载。我考虑过可能会像列表中一样将某些元素“上移”,但是没有像Remove() / AddFirst()或Move()这样的方法。
在BlockingCollection<>
中排列项目的首选方法是什么?
很遗憾,没有办法以您所期望的方式重新排列队列。您真正需要的是一个作为优先队列实现的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
它强制您使用多个队列,因此不是最优雅的解决方案。
BlockingCollection<T>
通过包装一个内部的IProducerConsumerCollection<T>
实现。默认情况下,它会在内部使用ConcurrentQueue<T>
,但你可以通过这个构造函数提供自己的实现。ConcurrentQueue<T>
集合封装到一个实现IProducerConsumerCollection<T>
的类中。这将允许你拥有“高优先级”和“低优先级”的元素。这是我使用它的方式:
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;
}
无法直接在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中实际存在的类型。它是基于下载项目的优先级调度,需要自己编写的内容。