2009-12-21 13 views
18

Estoy tratando de entender el propósito de BlockingCollection en el contexto de las nuevas pilas en paralelo en .NET 4.¿Cuál es el propósito de BlockingCollection (Of T)

La documentación MSDN dice:

BlockingCollection se utiliza como un contenedor para una instancia de IProducerConsumerCollection, lo que permite que los intentos de eliminación de la colección se bloqueen hasta que los datos estén disponibles para su eliminación. De forma similar, se puede crear un BlockingCollection para imponer un límite superior en la cantidad de elementos de datos permitidos en IProducerConsumerCollection; los intentos de adición a la colección pueden bloquearse hasta que haya espacio disponible para almacenar los elementos agregados.

Sin embargo cuando miro a la aplicación de algunas IProducerConsumerCollection, como ConcurrentQueue veo que proporcionan un cierre libre, hilo de seguridad, implementaciones. Entonces, ¿por qué es necesario el mecanismo de bloqueo que proporciona BlockingCollection? Todos los ejemplos en MSDN muestran el uso de esas colecciones a través de la envoltura BlockingCollection, ¿cuáles son los problemas de usar esas colecciones directamente? ¿Qué beneficio produce usar BlockingCollection?

Respuesta

17

El bloqueo hasta que la operación se puede realizar es una conveniencia si no tiene nada más que hacer de todos modos (o más bien: no puede continuar hasta que se haya realizado la operación).

Si tiene una cola sin bloqueo desde la que desea leer datos, y no hay datos en este momento, tiene que sondearla periódicamente, o esperar en algún semáforo, hasta que haya datos. Si la cola bloquea, eso ya está hecho automáticamente.

De forma similar, si intenta agregar una cola sin bloqueo que está llena, la operación simplemente fallará, y luego tendrá que decidir qué hacer. La cola de bloqueo solo esperará hasta que haya espacio.

Si tiene algo inteligente que hacer en lugar de esperar (como comprobar otra cola de datos o elevar una QueueTooFullException), entonces quiere la cola sin bloqueo, pero a menudo ese no es el caso.

A menudo, hay una forma de especificar un tiempo de espera en las colas de bloqueo.

+0

No puedo encontrarlo en ninguna parte. ¿Cuál es el significado de "bloqueo", es más bien "ignorar" "esperar hasta"? – Fulproof

+0

"bloqueo" significa "espere hasta que se complete la operación" – Thilo

7

El propósito del bloqueo es el propio bloqueo. Puede hacer que se lean varios hilos de la colección, y si no hay datos disponibles, el hilo permanecerá bloqueado hasta que lleguen nuevos datos.

Además, con la capacidad de establecer un límite de tamaño, puede dejar que el hilo del productor que está llenando la colección solo alimente tanto como pueda. Cuando la colección alcanza el límite, el hilo se bloqueará hasta que los hilos del consumidor hayan dejado espacio para los datos.

De esta manera puede utilizar la colección para acelerar el rendimiento de los datos, sin hacer ninguna comprobación usted mismo. Sus hilos solo leen y escriben todo lo que pueden, y la colección se encarga de mantener los hilos trabajando o durmiendo según sea necesario.

+3

La parte importante es "sin hacer ninguna comprobación usted mismo". Tanto el código de productor como el de consumidor pueden ser realmente simples, casi completamente iguales a los de su versión no paralela y aún así obtener el beneficio de que los hilos se queden dormidos si no hay nada (útil) que hacer por ellos. – VolkerK

4

Es una de esas cosas que es mucho más fácil de entender una vez que lo haces.

Para consumidores productores, tengamos dos objetos, Productor y Consumidor. Ambos comparten una cola que se les da cuando están construidos, para que puedan escribir entre ellos.

Adición de un consumidor productor es bastante familiar, sólo con la CompleteAdding un poco diferente:

public class Producer{ 
     private BlockingCollection<string> _queue; 
     public Producer(BlockingCollection<string> queue){_queue = queue;} 

     //a method to do something 
     public MakeStuff() 
     { 
      for(var i=0;i<Int.MaxValue;i++) 
      { 
       _queue.Add("a string!"); 
      } 

      _queue.CompleteAdding(); 
     } 
} 

El consumidor no parece tener sentido - hasta que se da cuenta de que el foreach no se detendrá el bucle hasta la cola ha completado la adición. Hasta entonces, si no hay elementos, simplemente volverá a dormir. Y dado que es la misma instancia de la colección en el productor y el consumidor, puede hacer que el consumidor SÓLO tome ciclos cuando hay cosas que hacer, y no tener que preocuparse por detenerlo, reiniciarlo, etc.

public class Consumer() 
{ 
     private BlockingCollection<string> _queue; 
     public Consumer(BlockingCollection<string> queue) 
     { 
      _queue = queue; 
     } 

     public void WriteStuffToFile() 
     { 
      //we'll hold until our queue is done. If we get stuff in the queue, we'll start processing it then 
      foreach(var s in _queue.GetConsumingEnumerable()) 
      { 
      WriteToFile(s); 
      } 
     } 
} 

Para que los conecte mediante el uso de la colección.

var queue = new BlockingCollection<string>(); 
var producer = new Producer(queue); 
var consumer = new Consumer(queue); 

producer.MakeStuff(); 
consumer.WriteStuffToFile(); 
+0

Olvidé agregar, la razón para hacer esto es que puedo poner al productor y al consumidor en hilos separados, y dejar el hilo principal para hacer otras cosas ahora. – Mathieson

Cuestiones relacionadas