2011-09-21 125 views
12

我有一個下載隊列與BlockingCollection<>實施。現在我想稍微優先考慮一下下載。我認爲將列表中的某些元素向上移動可能會很棒,就像在列表中一樣,但沒有像Remove()/ AddFirst()或Move()這樣的方法。元素順序BlockingCollection <>

BlockingCollection<>中安排物品的首選方式是什麼?

回答

8

BlockingCollection<T>作品通過包裝內部IProducerConsumerCollection<T>。缺省情況是在內部使用ConcurrentQueue<T>,但可以通過this constructor提供自己的實現。

如果您提供了自己的線程安全集合,則可以使用任何您想要的集合類型。這將允許您根據需要優先考慮元素。

儘管沒有內置集合可以實現您所需的功能,但您可能會將一對ConcurrentQueue<T>集合包裝到實現IProducerConsumerCollection<T>的類中。這將允許您擁有「高優先級」和「低優先級」元素。

5

無法直接在BlockingCollection<T>之上實施優先級隊列。 A 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框架中存在的類型。根據下載項目的優先級調度,您需要編寫自己的代碼。

15

不幸的是,沒有辦法以您想要的方式重新排列隊列。你真正需要的是作爲優先級隊列實現的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

裏德在告訴你需要實施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; 
}