2011-09-21 8 views
12

Mam kolejkę pobierania zaimplementowaną z BlockingCollection<>. Teraz chcę nadać priorytet niektórym plikom do pobrania raz na jakiś czas. Pomyślałem, że wspaniale byłoby przenieść niektóre elementy "do góry" Kolekcji, jak na liście, ale nie ma metody jak Remove()/AddFirst() lub Move().Kolejność elementów w BlockingCollection <>

Jaki jest preferowany sposób układania elementów w BlockingCollection<>?

Odpowiedz

8

BlockingCollection<T> działa przez owijanie wewnętrznego IProducerConsumerCollection<T>. Domyślnie używane jest wewnętrznie ConcurrentQueue<T>, ale możesz podać własną implementację za pomocą this constructor.

Jeśli udostępniasz swoją własną kolekcję wątków, możesz użyć dowolnego typu kolekcji, który chcesz. Umożliwiłoby to priorytetowe traktowanie elementów w razie potrzeby.

Chociaż nie ma wbudowanych kolekcji, które będą implementować pożądaną funkcjonalność, prawdopodobnie można owinąć parę kolekcji ConcurrentQueue<T> w klasę, która implementuje IProducerConsumerCollection<T>. Pozwoli to na posiadanie elementów "o wysokim priorytecie" i "o niskim priorytecie".

5

Nie ma możliwości zaimplementowania kolejki priorytetowej bezpośrednio nad numerem BlockingCollection<T>. A BlockingCollection<T> jest najlepiej postrzegana jako kolejka, dla której nie można dokonać zmiany kolejności.

Można jednak użyć kombinacji kolejki priorytetowej i BlockingCollection<T>, aby uzyskać ten sam efekt. Załóżmy, że przez sekundę zaimplementowałeś prosty PriorityQueue<T>, który poprawnie zamawia pobieranie. Poniższa mogą być wykorzystane, aby dodać priorytet obsługi po stronie odbiorczej

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

Uwaga: PriorityQueue<T> nie jest typem, który faktycznie istnieje w .NET Framework. Jest to coś, co musisz napisać samodzielnie na podstawie harmonogramu priorytetów pobranych elementów.

15

Niestety, nie ma sposobu, aby zmienić kolejność oczek w kolejce. To, czego naprawdę potrzebujesz, to PriorityBlockingCollection zaimplementowana jako kolejka priorytetowa, ale niestety też nie istnieje.

Co możesz zrobić, to wykorzystać metodę TakeFromAny, aby uzyskać pożądane zachowanie priorytetowe. TakeFromAny odkaże pierwszy dostępny element z tablicy instancji BlockingCollection. Da pierwszeństwo kolejkom wymienionym jako pierwsze w tablicy.

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

Powyższy przykład wypisze:

high1 
high2 
low1 
low2 

To zmusza do wykorzystania do wielu kolejek, więc nie jest to najbardziej eleganckie rozwiązanie.

3

Trzon mówi, że musisz zaimplementować IProducerConsumerCollection<T>. Istnieje jednak klasa, która może ci pomóc. Nie jest wbudowany, ale jest dostępny na MSDN. Po prostu przekaż ten ConcurrentPriorityQueue do swojego BlockingCollection.

ten sposób użyłem go:

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

ICommand jest interfejsem w moim projekcie.

Teraz ta pozwala na dodawanie elementów tak:

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

Produkty o niższej wartości całkowitej jako priorytetu realizowane będą w pierwszej kolejności. W powyższym przykładzie:

command1 
command3 
command2 

Gdy zapętlenie nad BlockingCollection, nie będziesz już się pojedyncze elementy (ICommand w moim przypadku), ale KeyValuePair. Oczywiście może to wymagać pewnych zmian w kodzie. Fajną rzeczą jest to, że masz oryginalny priorytet:

foreach (var command in _queue) 
{ 
    var priority = command.Key; 
    var actualCommand = command.Value; 
}