2011-09-21 8 views
12

Tengo una cola de descarga implementada con BlockingCollection<>. Ahora quiero priorizar algunas descargas de vez en cuando. Pensé que sería genial mover algunos elementos 'arriba' de la Colección, como en una lista, pero no hay ningún método como Eliminar()/AñadirFirst() o Mover().Orden de elemento en BlockingCollection <>

¿Cuál es la forma preferida de organizar los artículos en un BlockingCollection<>?

Respuesta

8

BlockingCollection<T> funciona envolviendo un interno IProducerConsumerCollection<T>. El valor predeterminado es usar un ConcurrentQueue<T> internamente, pero puede proporcionar su propia implementación a través del this constructor.

Si proporciona su propia colección de threadsafe, puede usar cualquier tipo de colección que desee. Esto le permitiría priorizar elementos según sea necesario.

Si bien no hay colecciones integradas que implementarán la funcionalidad deseada, probablemente podría envolver un par de colecciones ConcurrentQueue<T> en una clase que implemente IProducerConsumerCollection<T>. Esto le permitiría tener elementos de "alta prioridad" y "baja prioridad".

5

No hay forma de implementar una cola de prioridad directamente sobre un BlockingCollection<T>. Un BlockingCollection<T> se ve mejor como una cola estricta para la cual no se puede reordenar.

Sin embargo, podría utilizar una combinación de una cola de prioridad y BlockingCollection<T> para lograr el mismo efecto. Supongamos por un segundo que implementa un simple PriorityQueue<T> que ordena correctamente sus descargas. El siguiente podría ser usado para añadir prioridad a la manipulación de la parte receptora

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

Nota: PriorityQueue<T> no es un tipo que realmente existe en el .Net Framework. Es algo que necesitaría escribir usted mismo en función de la programación prioritaria de los elementos descargados.

15

Lamentablemente, no hay forma de reorganizar la cola de la manera que desee. Lo que realmente necesita es un PriorityBlockingCollection implementado como una cola de prioridad, pero lamentablemente eso tampoco existe.

Lo que puede hacer es explotar el método TakeFromAny para obtener el comportamiento de prioridad que desea. TakeFromAny quitará la cola del primer elemento disponible de una matriz de instancias BlockingCollection. Dará prioridad a las colas que se enumeran primero en la matriz.

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

El ejemplo anterior se imprimirá:

high1 
high2 
low1 
low2 

Te obliga a utilizar para múltiples colas por lo que no es la solución más elegante.

3

Reed tiene razón al decirle que debe implementar el IProducerConsumerCollection<T>. Sin embargo, hay una clase que puede ayudarte. No está incorporado, pero aparece en MSDN. Simplemente pase este ConcurrentPriorityQueue a su BlockingCollection.

Esto es como lo he usado:

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

El ICommand es una interfaz en mi proyecto.

Ahora bien, esto le permite añadir elementos como esto:

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

Los elementos con un valor entero inferior como prioridad se ejecutarán en primer lugar. En el ejemplo anterior:

command1 
command3 
command2 

Cuando bucle sobre su BlockingCollection, ya no obtendrá los elementos individuales (ICommand en mi caso), pero un KeyValuePair. Esto podría requerir algunos cambios de código, por supuesto. Lo bueno es que tiene su prioridad original:

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