Compartir a través de


Información general sobre BlockingCollection

BlockingCollection<T> es una clase de colección segura para subprocesos que proporciona las siguientes características:

  • Implementación del modelo productor-consumidor.

  • Operaciones simultáneas de agregar y quitar elementos en varios subprocesos.

  • Capacidad máxima opcional.

  • Operaciones de inserción y eliminación que se bloquean cuando la colección está vacía o completa.

  • Operación de inserción y eliminación "try" que no se bloquean o que se bloquean en un período de tiempo especificado.

  • Encapsula los tipos de colección que implementen IProducerConsumerCollection<T>

  • Cancelación con tokens de cancelación.

  • Dos tipos de enumeración con foreach (For Each en Visual Basic):

    1. Enumeración de solo lectura.

    2. Enumeración que quita los elementos a medida que se van enumerando.

Compatibilidad con delimitación y bloqueo

BlockingCollection<T> admite los enlaces y bloqueos. Delimitación significa que el usuario puede establecer la capacidad máxima de la colección. La delimitación es importante en ciertos casos porque permite controlar el tamaño máximo de la colección en memoria y evita que los subprocesos productores vayan muy por delante de los subprocesos consumidores.

Varios subprocesos o tareas pueden agregar elementos a la colección simultáneamente y, si la colección alcanza la capacidad máxima especificada, los subprocesos productores se bloquean hasta que se quite algún elemento. Varios consumidores pueden quitar elementos simultáneamente y, si la colección se vacía, los subprocesos consumidores se bloquean hasta que un productor agregue un elemento. Un subproceso productor puede llamar al método CompleteAdding para indicar que no se van a agregar más elementos. Los consumidores supervisan la propiedad IsCompleted para determinar cuándo la colección está vacía y no se van a agregar más elementos. En el siguiente ejemplo se muestra una BlockingCollection sencilla con una capacidad limitada de 100. Una tarea de productor agrega elementos a la colección siempre que alguna condición externa sea true, y después llama a CompleteAdding. La tarea de consumidor toma elementos hasta que el valor de la propiedad IsCompleted sea true.

' A bounded collection. It can hold no more 
' than 100 items at once.
Dim dataItems = New BlockingCollection(Of Data)(100)

' A simple blocking consumer with no cancellation.
Task.Factory.StartNew(Sub()
                          While dataItems.IsCompleted = False
                              Dim dataItem As Data = Nothing
                              Try
                                  dataItem = dataItems.Take()
                              Catch e As InvalidOperationException
                                  ' IOE means that Take() was called on a completed collection.
                                  ' In this example, we can simply catch the exception since the 
                                  ' loop will break on the next iteration.
                              End Try
                              If (dataItem IsNot Nothing) Then
                                  Process(dataItem)
                              End If
                          End While
                          Console.WriteLine(vbCrLf & "No more items to take.")
                      End Sub)

' A simple blocking producer with no cancellation.
Task.Factory.StartNew(Sub()
                          While moreItemsToAdd = True
                              Dim item As Data = GetData()

                              ' Blocks if numbers.Count = dataItems.BoundedCapacity
                              dataItems.Add(item)
                          End While

                          ' Let consumer know we are done.
                          dataItems.CompleteAdding()
                      End Sub)
            // A bounded collection. It can hold no more 
            // than 100 items at once.
            BlockingCollection<Data> dataItems = new BlockingCollection<Data>(100);


            // A simple blocking consumer with no cancellation.
            Task.Factory.StartNew(() => 
            {
                while (!dataItems.IsCompleted)
                {

                    Data data = null;
                    // Blocks if number.Count == 0
                    // IOE means that Take() was called on a completed collection.
                    // Some other thread can call CompleteAdding after we pass the
                    // IsCompleted check but before we call Take. 
                    // In this example, we can simply catch the exception since the 
                    // loop will break on the next iteration.
                    try
                    {
                        data = dataItems.Take();
                    }
                    catch (InvalidOperationException) { }

                    if (data != null)
                    {
                        Process(data);
                    }
                }
                Console.WriteLine("\r\nNo more items to take.");
            });

            // A simple blocking producer with no cancellation.
            Task.Factory.StartNew(() =>
            {
                while (moreItemsToAdd)
                {
                    Data data = GetData();
                    // Blocks if numbers.Count == dataItems.BoundedCapacity
                    dataItems.Add(data);
                }
                // Let consumer know we are done.
                dataItems.CompleteAdding();
            });

Para obtener un ejemplo completo, vea Cómo: Agregar y tomar elementos de forma individual en una clase BlockingCollection.

Operaciones de bloqueo con tiempo de espera especificado

En operaciones TryAdd y TryTake de bloqueo con tiempo de espera especificado en colecciones delimitadas, el método intenta agregar o quitar un elemento. Si un elemento está disponible se coloca en la variable que se pasó por referencia, y el método devuelve true. Si no se recupera ningún elemento después de un tiempo de espera especificado, el método devuelve false. Después, el subproceso puede hacer otro trabajo útil antes de intentar tener acceso a la colección de nuevo. Para obtener un ejemplo de acceso de bloqueo con tiempo de espera especificado, vea el segundo ejemplo de Cómo: Agregar y tomar elementos de forma individual en una clase BlockingCollection.

Cancelar operaciones de agregar y quitar

Las operaciones de agregar y quitar se realizan normalmente en un bucle. Para cancelar un bucle, pase un CancellationToken al método TryTake o TryAdd y, a continuación, compruebe el valor de la propiedad IsCancellationRequested del token en cada iteración. Si el valor es true, puede responder a la solicitud de cancelación limpiando los recursos y saliendo del bucle. En el siguiente ejemplo, se muestran una sobrecarga del método TryAdd que toma un token de cancelación, y el código que lo utiliza:

Do While moreItems = True
    ' Cancellation causes OCE. We know how to handle it.
    Try
        success = bc.TryAdd(itemToAdd, 2, ct)
    Catch ex As OperationCanceledException
        bc.CompleteAdding()
        Exit Do
    End Try
Loop
do
{
    // Cancellation causes OCE. We know how to handle it.
    try
    {
        success = bc.TryAdd(itemToAdd, 2, ct);
    }
    catch (OperationCanceledException)
    {
        bc.CompleteAdding();
        break;
    }
    //...
} while (moreItems == true);

Para obtener un ejemplo de cómo agregar compatibilidad con las cancelaciones, vea el segundo ejemplo de Cómo: Agregar y tomar elementos de forma individual en una clase BlockingCollection.

Especificar el tipo de colección

Cuando crea una clase BlockingCollection<T>, puede especificar la capacidad limitada así como el tipo de colección que se va a utilizar. Por ejemplo, podría especificar primero una clase ConcurrentConcurrentQueue para un comportamiento FIFO (PEPS, "primero en entrar, primero en salir") o una clase ConcurrentStack<T> para un comportamiento LIFO (UEPS "último en entrar, primero en salir". Puede usar cualquier clase de colección que implemente la interfaz IProducerConsumerCollection<T>. EL tipo de colección predeterminado para BlockingCollection<T> es ConcurrentQueue<T>. En el siguiente ejemplo de código, se muestra cómo crear una colección BlockingCollection<T> de cadenas que tiene una capacidad de 1000 y utiliza una colección ConcurrentBag:

Dim bc = New BlockingCollection(Of String)(New ConcurrentBag(Of String()), 1000)
BlockingCollection<string> bc = new BlockingCollection<string>(new ConcurrentBag<string>(), 1000 );

Para obtener más información, vea Cómo: Agregar la funcionalidad de límite y bloqueo a una clase de colección.

Compatibilidad con IEnumerable

BlockingCollection<T> proporciona un método GetConsumingEnumerable que permite a los consumidores utilizar foreach (For Each en Visual Basic) para quitar los elementos hasta que la colección esté completa, lo que significa que está vacía y no se agregará ningún elemento. Para obtener más información, vea Cómo: Utilizar ForEach para quitar elementos de BlockingCollection.

Utilizar muchas BlockingCollections como una

Para los escenarios en los que un consumidor necesita elementos de varias colecciones simultáneamente, puede crear matrices de BlockingCollection<T> y utilizar métodos estáticos como TakeFromAny y AddToAny que agregarán o quitarán elementos de cualquiera de las colecciones de la matriz. Si una colección produce un bloqueo, el método prueba con otra inmediatamente, hasta que encuentra una que puede realizar la operación. Para obtener más información, vea Cómo: Usar matrices de colecciones de bloqueo en una canalización.

Vea también

Referencia

System.Collections.Concurrent

Conceptos

Colecciones seguras para subprocesos

Otros recursos

Colecciones y estructuras de datos