2011-09-21 15 views
12

BlockingCollection<>で実装されたダウンロードキューがあります。今私はいくつかのダウンロードに優先順位をつけたいと思っています。 Remove()/ AddFirst()やMove()のようなメソッドはありません。リストのように、いくつかの要素をCollectionの上に移動することは素晴らしいかもしれません。BlockingCollectionの要素の順序<>

BlockingCollection<>にアイテムを配置する好ましい方法は何ですか?

答えて

8

BlockingCollection<T>は、IProducerConsumerCollection<T>の内部をラップして動作します。デフォルトでは内部でConcurrentQueue<T>を使用していますが、this constructorで独自の実装を提供できます。

独自のスレッドセーフコレクションを提供する場合は、任意のコレクションタイプを使用できます。これにより、必要に応じて要素に優先順位を付けることができます。

目的の機能を実装する組み込みコレクションはありませんが、ConcurrentQueue<T>コレクションのペアをIProducerConsumerCollection<T>を実装するクラスにラップすることができます。これにより、「高い優先度」と「低い優先度」の要素を持つことができます。

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

注の取り扱いに優先順位を追加するために使用することができる:実際の.NET Frameworkに存在するタイプがPriorityQueue<T>ありません。ダウンロードしたアイテムの優先順位付けに基づいて自分自身を作成する必要があります。

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に掲載されています。このConcurrentPriorityQueueBlockingCollectionに渡すだけです。

これは私がそれを使用する方法である:

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; 
} 
関連する問題