Flujo de datos (biblioteca TPL)

.NET Framework (current version)
 

Publicado: octubre de 2016

La biblioteca (TPL) proporciona componentes de flujo de datos para aumentar la solidez de aplicaciones con la simultaneidad habilitada. Estos componentes de flujo de datos se conocen colectivamente como la biblioteca de TPL Dataflow. Este modelo de flujo de datos promueve una programación basada en actores mediante el paso de mensajes en proceso para tareas de canalización y de flujo de datos de grano grueso. Los componentes de flujo de datos se basan en los tipos y la infraestructura de programación de la biblioteca TPL y se integran con la compatibilidad de los lenguajes C#, Visual Basic y F# para proporcionar programación asincrónica. Estos componentes de flujo de datos son útiles cuando se tienen varias operaciones que deben comunicarse entre sí de forma asincrónica, o cuando se desea procesar datos a medida que estén disponibles. Por ejemplo, piense en una aplicación que procesa datos de imagen de una cámara web. Con el modelo de flujo de datos, la aplicación puede procesar fotogramas de imagen a medida que estén disponibles. Si la aplicación mejora fotogramas de imagen, por ejemplo, mediante la realización de una corrección o reduciendo ojos rojos, puede crear un canalización de componentes de flujo de datos. Cada fase de la canalización puede utilizar más funcionalidad de paralelismo de grano grueso, como la funcionalidad proporcionada por la biblioteca TPL, para transformar la imagen.

En este documento se proporciona información general sobre la biblioteca de flujos de datos TPL. Se describe el modelo de programación, los tipos de bloques de flujo de datos predefinidos y cómo configurar bloques de flujo de datos para satisfacer las necesidades específicas de las aplicaciones.

System_CAPS_ICON_tip.jpg Sugerencia

La biblioteca de TPL Dataflow (System.Threading.Tasks.Dataflow espacio de nombres) no se distribuye con el .NET Framework 4.5. Para instalar el System.Threading.Tasks.Dataflow espacio de nombres, abra el proyecto en Visual Studio 2012, elija administrar paquetes de NuGet en el menú proyecto y busque en línea el Microsoft.Tpl.Dataflow paquete.

Este documento contiene las siguientes secciones:

La biblioteca de flujos de datos TPL proporciona una base para el paso de mensajes y para paralelizar aplicaciones que consumen mucha CPU, así como aplicaciones intensivas de entrada y salida con alto rendimiento y latencia baja. También ofrece el control explícito sobre cómo almacenar los datos en búfer y desplazarlos alrededor del sistema. Para entender mejor el modelo de programación de flujo de datos, piense en una aplicación que de forma asincrónica carga imágenes desde el disco y crea un compuesto de esas imágenes. Los modelos de programación tradicionales suelen usar devoluciones de llamada y objetos de sincronización, como bloqueos, para coordinar tareas y tener acceso a datos compartidos. Con el modelo de programación de flujo de datos, puede crear objetos de flujo de datos que procesan las imágenes como se leen desde el disco. Bajo el modelo de flujo de datos, se declara cómo se controlan los datos cuando están disponibles, así como las dependencias entre datos. Dado que el runtime administra las dependencias entre datos, se puede evitar a menudo la necesidad de sincronizar el acceso a los datos compartidos. Además, dado que el runtime programa el trabajo según la llegada asincrónica de datos, el flujo de datos puede mejorar la capacidad de respuesta y el nivel de rendimiento administrando de forma eficaz los subprocesos subyacentes. Para obtener un ejemplo que utiliza el modelo de programación de flujo de datos para implementar el procesamiento de imágenes en una aplicación de formularios Windows Forms, vea Tutorial: usar el flujo de datos en una aplicación de Windows Forms.

Orígenes y destinos

La biblioteca de flujo de datos TPL consta de bloques de flujo de datos, que son estructuras de datos ese búfer y procesar datos. La biblioteca TPL define tres tipos de bloques de flujo de datos: bloques de origen, bloques de destino, y bloques propagadores. Un bloque de origen actúa como un origen de datos y se puede leer desde él. Un bloque de destino actúa como un receptor de datos y se puede escribir en él. Un bloque propagador actúa como un bloque de origen y un bloque de destino, y se puede leer desde él y escribir en él. La biblioteca TPL define la System.Threading.Tasks.Dataflow.ISourceBlock<> </> > interfaz para representar orígenes, System.Threading.Tasks.Dataflow.ITargetBlock<> </> > para representar destinos y System.Threading.Tasks.Dataflow.IPropagatorBlock<TInput, TOutput> para representar propagadores.</TInput, TOutput> IPropagatorBlock<TInput, TOutput> hereda de ISourceBlock<>>, y ITargetBlock<>>.</TInput, TOutput>

La biblioteca de flujo de datos TPL proporciona varios tipos de bloques de flujo de datos predefinidos que implementan la ISourceBlock<>>, ITargetBlock<>>, y IPropagatorBlock<TInput, TOutput> interfaces.</TInput, TOutput> Estos tipos de bloques de flujo de datos se describen en este documento en la sección tipos de bloques de flujo de datos predefinidos.

Conectar bloques

Puede conectar los bloques de flujo de datos al formulario canalizaciones, que son secuencias lineales de bloques de flujo de datos, o redes, que son gráficos de bloques de flujo de datos. Una canalización es una forma de red. En una canalización o red, los orígenes propagan datos de forma asincrónica en destinos a medida que esos datos están disponibles. El ISourceBlock<>>. Enlacesa método vincula un bloque de origen a un bloque de destino. Un origen puede vincularse a cero o más destinos; los destinos se pueden vincular a partir de cero o más orígenes. Puede agregar o quitar bloques de flujo de datos hacia o desde una canalización o red simultáneamente. Los tipos de bloques de flujo de datos predefinidos controlan todos los aspectos de la seguridad para subprocesos de vinculación y desvinculación.

Para obtener un ejemplo que conecta los bloques de flujo de datos para formar una canalización básica, vea Tutorial: crear una canalización de flujo de datos. Para obtener un ejemplo que conecta los bloques de flujo de datos para formar una red más compleja, vea Tutorial: usar el flujo de datos en una aplicación de Windows Forms. Para obtener un ejemplo que desvincula un destino de un origen después de que el origen proporciona el destino de un mensaje, vea Cómo: desvincular bloques de flujo de datos.

Filtrado

Cuando se llama a la ISourceBlock<>>. Enlacesa método para vincular un origen a un destino, puede proporcionar un delegado que determina si el bloque de destino acepta o rechaza un mensaje basándose en el valor de ese mensaje. Este mecanismo de filtrado resulta útil para garantizar que un bloque de flujo de datos recibe solo ciertos valores. Para la mayoría de los tipos de bloques de flujo de datos predefinidos, si un bloque de origen está conectado a varios bloques de destino, cuando un bloque de destino rechaza un mensaje, el origen proporciona ese mensaje al destino siguiente. El orden en el que un origen proporciona mensajes a los destinos se define mediante el origen y puede variar según el tipo de origen. La mayoría de los tipos de bloques de origen dejan de proporcionar un mensaje después de que un destino acepta ese mensaje. Una excepción a esta regla es la BroadcastBlock<> </> > (clase), que proporciona cada mensaje a todos los destinos, aunque algunos destinos rechacen el mensaje. Para obtener un ejemplo que usa el filtrado para procesar únicamente determinados mensajes, consulte Tutorial: usar el flujo de datos en una aplicación de Windows Forms.

System_CAPS_ICON_important.jpg Importante

Dado que cada tipo de bloque de flujo de datos de origen predefinido garantiza que los mensajes se propaguen en el orden en que se reciben, se debe leer cada mensaje desde el bloque de origen antes de que el bloque de origen pueda procesar el mensaje siguiente. Por consiguiente, si usa el filtrado para conectarse varios destinos a un origen, asegúrese de que al menos un bloque de destino recibe cada mensaje. De lo contrario, la aplicación podría generar un interbloqueo.

Paso de mensajes

El modelo de programación de flujo de datos está relacionado con el concepto de pasar mensajes, donde los componentes independientes de un programa comunican entre sí enviándose mensajes. Una manera de propagar mensajes entre los componentes de la aplicación es llamar a la registrar<> </> > y DataflowBlock.SendAsync<> </> > métodos para enviar mensajes a los bloques de flujo de datos de destino (registrar<> </> > actúa de forma sincrónica; SendAsync<> </> > actúa de forma asincrónica) y la recepción<>>, ReceiveAsync<>>, y TryReceive<> </> > métodos para recibir mensajes de los bloques de origen. Puede combinar estos métodos con las canalizaciones o redes de flujo de datos enviando datos de entrada al nodo principal (un bloque de destino) y recibiendo datos de salida del nodo terminal de la canalización o de los nodos terminales de la red (uno o varios bloques de origen). También puede utilizar el elegir método para leer desde el primero de los orígenes proporcionados siempre que tenga datos disponibles y realice acciones sobre esos datos.

Bloques de origen proporcionan datos a los bloques de destino mediante una llamada a la ITargetBlock<>>. OfferMessage método. El bloque de destino responde a un mensaje proporcionado de una de estas tres maneras: puede aceptar el mensaje, rechazar el mensaje o posponer el mensaje. Cuando el destino acepta el mensaje, el OfferMessage método devuelve aceptado. Cuando el destino rechaza el mensaje, el OfferMessage método devuelve rechazada. Cuando el destino requiere que ya no recibe los mensajes del origen de OfferMessage devuelve DecliningPermanently. Los tipos de bloques de origen predefinidos no proporcionan mensajes a los destinos vinculados después de recibir este valor devuelto, y automáticamente se desvinculan de estos destinos.

Cuando un bloque de destino pospone el mensaje para su uso posterior, el OfferMessage método devuelve pospuesto. Un bloque de destino que pospone un mensaje puede llamar posteriormente el ISourceBlock<>>. ReserveMessage método para intentar reservar el mensaje proporcionado. En este punto, el mensaje todavía permanece disponible y lo puede usar el bloque de destino, o puede que otro destino haya tomado el mensaje. Cuando el bloque de destino requiere el mensaje posteriormente o ya no necesita el mensaje, llama a la ISourceBlock<>>. ConsumeMessage o ReleaseReservation método, respectivamente. La reserva de mensajes la utilizan normalmente los tipos de bloques de flujo de datos que trabajan en modo no expansivo. El modo no expansivo se explica más adelante en este documento. En lugar de reservar un mensaje pospuesto, un bloque de destino también puede utilizar el ISourceBlock<>>. ConsumeMessage método para intentar utilizar directamente el mensaje pospuesto.

Finalización del bloque de flujo de datos

Bloques de flujo de datos también admiten el concepto de finalización. Un bloque de flujo de datos que está en estado completado no realiza ningún trabajo posterior. Cada bloque de flujo de datos tiene asociado un System.Threading.Tasks.Task objeto, conocido como un tarea de finalización, que representa el estado de finalización del bloque. Dado que puede esperar un tarea para terminar, mediante tareas de finalización, puede esperar uno o más nodos terminales de un flujo de datos de red a fin de objeto. El IDataflowBlock interfaz define el Complete método, que informa al bloque de flujo de datos de una solicitud para que se complete, y el finalización propiedad, que devuelve la tarea de finalización para el bloque. Ambos ISourceBlock<> </> > y ITargetBlock<> </> > heredan la IDataflowBlock interfaz.

Hay dos maneras de determinar si un bloque de flujo de datos se completó sin error, encontró uno o más errores, o se canceló. La primera manera es llamar a la Task.Wait método en la tarea de finalización de un try - catch bloque (Try - Catch en Visual Basic). En el ejemplo siguiente se crea un ActionBlock<> </> > objeto que produce ArgumentOutOfRangeException si el valor de entrada es menor que cero. AggregateException se produce cuando se llama a este ejemplo esperar en la tarea de finalización. El ArgumentOutOfRangeException se tiene acceso mediante la InnerExceptions propiedad de la AggregateException objeto.

         // Create an ActionBlock<int> object that prints its input
         // and throws ArgumentOutOfRangeException if the input
         // is less than zero.
         var throwIfNegative = new ActionBlock<int>(n =>
         {
            Console.WriteLine("n = {0}", n);
            if (n < 0)
            {
               throw new ArgumentOutOfRangeException();
            }
         });

         // Post values to the block.
         throwIfNegative.Post(0);
         throwIfNegative.Post(-1);
         throwIfNegative.Post(1);
         throwIfNegative.Post(-2);
         throwIfNegative.Complete();

         // Wait for completion in a try/catch block.
         try
         {
            throwIfNegative.Completion.Wait();
         }
         catch (AggregateException ae)
         {
            // If an unhandled exception occurs during dataflow processing, all
            // exceptions are propagated through an AggregateException object.
            ae.Handle(e =>
            {
               Console.WriteLine("Encountered {0}: {1}", 
                  e.GetType().Name, e.Message);
               return true;
            });
         }

         /* Output:
         n = 0
         n = -1
         Encountered ArgumentOutOfRangeException: Specified argument was out of the range
          of valid values.
         */

En este ejemplo se muestra el caso en el que una excepción no está controlada en el delegado de un bloque de flujo de datos de ejecución. Se recomienda controlar las excepciones en los cuerpos de estos bloques. Sin embargo, si no puede hacerlo, el bloque se comporta como si estuviera cancelado y no procesa los mensajes entrantes.

Cuando un bloque de flujo de datos se cancela explícitamente, el AggregateException objeto contiene OperationCanceledException en el InnerExceptions propiedad. Para obtener más información sobre la cancelación del flujo de datos, vea Habilitar la cancelación más adelante en este documento.

La segunda manera de determinar el estado de finalización de un bloque de flujo de datos es usar una continuación fuera de la tarea de finalización o utilizar las características de lenguaje asincrónicas de C# y Visual Basic para esperar a la tarea de finalización de forma asincrónica. El delegado que proporciona a la Task.ContinueWith método toma un tarea objeto que representa la tarea anterior. En el caso de la finalización propiedad, el delegado de continuación toma la propia tarea de finalización. En el siguiente ejemplo se parece el anterior, excepto que también utiliza la ContinueWith método para crear una tarea de finalización que imprime el estado de la operación de flujo de datos global.

         // Create an ActionBlock<int> object that prints its input
         // and throws ArgumentOutOfRangeException if the input
         // is less than zero.
         var throwIfNegative = new ActionBlock<int>(n =>
         {
            Console.WriteLine("n = {0}", n);
            if (n < 0)
            {
               throw new ArgumentOutOfRangeException();
            }
         });

         // Create a continuation task that prints the overall 
         // task status to the console when the block finishes.
         throwIfNegative.Completion.ContinueWith(task =>
         {
            Console.WriteLine("The status of the completion task is '{0}'.", 
               task.Status);
         });

         // Post values to the block.
         throwIfNegative.Post(0);
         throwIfNegative.Post(-1);
         throwIfNegative.Post(1);
         throwIfNegative.Post(-2);
         throwIfNegative.Complete();

         // Wait for completion in a try/catch block.
         try
         {
            throwIfNegative.Completion.Wait();
         }
         catch (AggregateException ae)
         {
            // If an unhandled exception occurs during dataflow processing, all
            // exceptions are propagated through an AggregateException object.
            ae.Handle(e =>
            {
               Console.WriteLine("Encountered {0}: {1}",
                  e.GetType().Name, e.Message);
               return true;
            });
         }

         /* Output:
         n = 0
         n = -1
         The status of the completion task is 'Faulted'.
         Encountered ArgumentOutOfRangeException: Specified argument was out of the range
          of valid values.
         */

También puede usar propiedades como IsCanceled en el cuerpo de la tarea de continuación para determinar información adicional sobre el estado de finalización de un bloque de flujo de datos. Para obtener más información sobre las tareas de continuación y cómo se relacionan con cancelación y control de errores, vea encadenar tareas mediante tareas de continuación, cancelación de tareas, Exception Handling, y NIB: Cómo: controlar excepciones iniciadas por tareas.

[go to top]

La biblioteca de flujos de datos TPL proporciona varios tipos de bloques de flujo de datos predefinidos. Estos tipos se dividen en tres categorías: el búfer bloques, bloques de ejecución, y bloques de agrupación. En las secciones siguientes se describen los tipos de bloques que componen estas categorías.

Bloques de almacenamiento en búfer

Los bloques de almacenamiento en búfer contiene datos para su uso por los consumidores de datos. La biblioteca de flujo de datos TPL proporciona tres tipos de bloques de almacenamiento en búfer: System.Threading.Tasks.Dataflow.BufferBlock<>>, System.Threading.Tasks.Dataflow.BroadcastBlock<>>, y System.Threading.Tasks.Dataflow.WriteOnceBlock<>>.

BufferBlock(T)

El BufferBlock<> </> > clase representa una estructura de mensajería asincrónica de uso general. Esta clase almacena una cola FIFO (primero en entrar, primero en salir) de mensajes donde varios orígenes pueden escribir o de los que varios destinos pueden leer. Cuando un destino recibe un mensaje desde una BufferBlock<> </> > objeto, ese mensaje se quita de la cola de mensajes. Por lo tanto, aunque un BufferBlock<> </> > objeto puede tener varios destinos, solo uno recibirá cada mensaje. El BufferBlock<> </> > clase es útil si desea pasar varios mensajes a otro componente, y ese componente debe recibir cada mensaje.

El siguiente ejemplo básico se expone varios Int32 valores para una BufferBlock<> </> > de objetos y, a continuación, se leen esos valores desde ese objeto.

         // Create a BufferBlock<int> object.
         var bufferBlock = new BufferBlock<int>();
         
         // Post several messages to the block.
         for (int i = 0; i < 3; i++)
         {
            bufferBlock.Post(i);
         }

         // Receive the messages back from the block.
         for (int i = 0; i < 3; i++)
         {
            Console.WriteLine(bufferBlock.Receive());
         }
         
         /* Output:
            0
            1
            2
          */

Para obtener un ejemplo completo que muestra cómo escribir y leer los mensajes de una BufferBlock<> </> > de objeto, vea Cómo: escribir mensajes y leer los mensajes de Dataflow Block.

BroadcastBlock(T)

El BroadcastBlock<> </> > clase es útil si debe pasar varios mensajes a otro componente, pero este componente necesita sólo el valor más reciente. Esta clase también resulta útil si desea difundir un mensaje a varios componentes.

El siguiente ejemplo básico se publica un doble valor a un BroadcastBlock<> </> > objeto y, a continuación, lee ese valor desde el objeto varias veces. Dado que no se quitan los valores de BroadcastBlock<> </> > objetos después de leerlos, el mismo valor está disponible cada vez.

         // Create a BroadcastBlock<double> object.
         var broadcastBlock = new BroadcastBlock<double>(null);

         // Post a message to the block.
         broadcastBlock.Post(Math.PI);

         // Receive the messages back from the block several times.
         for (int i = 0; i < 3; i++)
         {
            Console.WriteLine(broadcastBlock.Receive());
         }

         /* Output:
            3.14159265358979
            3.14159265358979
            3.14159265358979
          */

Para obtener un ejemplo completo que muestra cómo utilizar BroadcastBlock<> </> > para difundir un mensaje a varios bloques de destino, consulte Cómo: especificar un programador de tareas en un Dataflow Block.

WriteOnceBlock(T)

El WriteOnceBlock<> </> > clase es similar a la BroadcastBlock<> </> > clase, salvo que un WriteOnceBlock<> </> > objeto puede escribirse en una sola vez. Puede pensar en WriteOnceBlock<> </> > es similar a la de C# readonly (ReadOnly en Visual Basic) (palabra clave), salvo que un WriteOnceBlock<> </> > objeto se vuelve inalterable después de recibir un valor en lugar de construcción. Como el BroadcastBlock<> </> > (clase), cuando un destino recibe un mensaje desde una WriteOnceBlock<> </> > objeto, ese mensaje no se quita de dicho objeto. Por tanto, varios destinos reciben una copia del mensaje. El WriteOnceBlock<> </> > clase resulta útil si desea difundir solamente el primero de varios mensajes.

El siguiente ejemplo básico se expone varios cadena valores para una WriteOnceBlock<> </> > objeto y, a continuación, lee el valor de dicho objeto. Dado que un WriteOnceBlock<> </> > objeto se puede escribir una vez, después de un WriteOnceBlock<> </> > objeto recibe un mensaje, descarta los mensajes subsiguientes.

         // Create a WriteOnceBlock<string> object.
         var writeOnceBlock = new WriteOnceBlock<string>(null);

         // Post several messages to the block in parallel. The first 
         // message to be received is written to the block. 
         // Subsequent messages are discarded.
         Parallel.Invoke(
            () => writeOnceBlock.Post("Message 1"),
            () => writeOnceBlock.Post("Message 2"),
            () => writeOnceBlock.Post("Message 3"));

         // Receive the message from the block.
         Console.WriteLine(writeOnceBlock.Receive());

         /* Sample output:
            Message 2
          */

Para obtener un ejemplo completo que muestra cómo utilizar WriteOnceBlock<> </> > para recibir el valor de la primera operación que finaliza, consulte Cómo: desvincular bloques de flujo de datos.

Bloques de ejecución

Los bloques de ejecución llaman a un delegado proporcionado por el usuario para cada fragmento de datos recibidos. La biblioteca de flujo de datos TPL proporciona tres tipos de bloques de ejecución: ActionBlock<>>, System.Threading.Tasks.Dataflow.TransformBlock<TInput, TOutput>, y System.Threading.Tasks.Dataflow.TransformManyBlock<TInput, TOutput>.</TInput, TOutput> </TInput, TOutput>

ActionBlock(T)

El ActionBlock<> </> > clase es un bloque de destino que llama a un delegado cuando recibe datos. Piense en un ActionBlock<> </> > objeto como un delegado que se ejecuta de forma asincrónica cuando los datos están disponibles. El delegado que se proporciona a un ActionBlock<> </> > objeto puede ser de tipo acción o tipo System.Func<TInput, Task>. Cuando se usa un ActionBlock<> </> > objeto con acción, procesamiento de cada elemento de entrada se considera completada cuando vuelve el delegado. Cuando se usa un ActionBlock<> </> > objeto con System.Func<TInput, Task>, procesamiento de cada elemento de entrada se considera completado solamente cuando el valor devuelto tarea objeto se complete. Mediante estos dos mecanismos, puede usar ActionBlock<> </> > para el procesamiento sincrónico y asincrónico de cada elemento de entrada.

El siguiente ejemplo básico se expone varios Int32 valores para una ActionBlock<> </> > objeto. El ActionBlock<> </> > objeto imprime esos valores en la consola. Después, en este ejemplo se establece el bloque en estado completado y se espera hasta que finalicen todas las tareas de flujo de datos.

         // Create an ActionBlock<int> object that prints values
         // to the console.
         var actionBlock = new ActionBlock<int>(n => Console.WriteLine(n));

         // Post several messages to the block.
         for (int i = 0; i < 3; i++)
         {
            actionBlock.Post(i * 10);
         }

         // Set the block to the completed state and wait for all 
         // tasks to finish.
         actionBlock.Complete();
         actionBlock.Completion.Wait();

         /* Output:
            0
            10
            20
          */

Para obtener ejemplos completos que muestran cómo utilizar delegados con la ActionBlock<> </> > de clases, consulte Cómo: realizar acción cuando un flujo de datos recibe datos del bloque.

TransformBlock(TInput, TOutput)

El TransformBlock<TInput, TOutput> clase es similar a la ActionBlock<> </> > clase salvo que actúa como origen y como destino.</TInput, TOutput> El delegado que se pasa a un TransformBlock<TInput, TOutput> objeto devuelve un valor de tipo TOutput.</TInput, TOutput> El delegado que se proporciona a un TransformBlock<TInput, TOutput> objeto puede ser de tipo System.Func<TInput, TOutput> o tipo System.Func<TInput, Task>.</TInput, TOutput> Cuando se usa un TransformBlock<TInput, TOutput> objeto con System.Func<TInput, TOutput>, procesamiento de cada elemento de entrada se considera completada cuando vuelve el delegado.</TInput, TOutput> Cuando se usa un TransformBlock<TInput, TOutput> objeto usado con System.Func<TInput, Task<TOutput>>, procesamiento de cada elemento de entrada se considera completado solamente cuando el valor devuelto tarea objeto se ha completado.</TInput, TOutput> Al igual que con ActionBlock<>>, mediante el uso de estos dos mecanismos, puede usar TransformBlock<TInput, TOutput> para el procesamiento sincrónico y asincrónico de cada elemento de entrada.</TInput, TOutput>

El siguiente ejemplo básico se crea un TransformBlock<TInput, TOutput> objeto que calcula la raíz cuadrada de la entrada.</TInput, TOutput> El TransformBlock<TInput, TOutput> objeto toma Int32 valores como entrada y genera doble valores como salida.</TInput, TOutput>

         // Create a TransformBlock<int, double> object that 
         // computes the square root of its input.
         var transformBlock = new TransformBlock<int, double>(n => Math.Sqrt(n));

         // Post several messages to the block.
         transformBlock.Post(10);
         transformBlock.Post(20);
         transformBlock.Post(30);

         // Read the output messages from the block.
         for (int i = 0; i < 3; i++)
         {
            Console.WriteLine(transformBlock.Receive());
         }

         /* Output:
            3.16227766016838
            4.47213595499958
            5.47722557505166
          */

Para obtener ejemplos completos que usa TransformBlock<TInput, TOutput> en una red de bloques de flujo de datos que realiza el procesamiento de imágenes en una aplicación de formularios Windows Forms, vea Tutorial: usar el flujo de datos en una aplicación de Windows Forms.</TInput, TOutput>

TransformManyBlock(TInput, TOutput)

El TransformManyBlock<TInput, TOutput> clase es similar a la TransformBlock<TInput, TOutput> clase salvo que TransformManyBlock<TInput, TOutput> produce cero o más valores de salida para cada valor de entrada, en lugar de solo uno valor de salida por cada valor de entrada.</TInput, TOutput> </TInput, TOutput> </TInput, TOutput> El delegado que se proporciona a un TransformManyBlock<TInput, TOutput> objeto puede ser de tipo System.Func<TInput, IEnumerable<TOutput>> o type System.Func<TInput, Task<IEnumerable<TOutput>>>.</TInput, TOutput> Cuando se usa un TransformManyBlock<TInput, TOutput> objeto con System.Func<TInput, IEnumerable<TOutput>>, procesamiento de cada elemento de entrada se considera completada cuando vuelve el delegado.</TInput, TOutput> Cuando se usa un TransformManyBlock<TInput, TOutput> objeto con System.Func<TInput, Task<IEnumerable<TOutput>>>, procesamiento de cada elemento de entrada se considera completado solo cuando el valor devuelto System.Threading.Tasks.Task<IEnumerable<TOutput>> objeto se ha completado.</TInput, TOutput>

El siguiente ejemplo básico se crea un TransformManyBlock<TInput, TOutput> objeto que divide las cadenas en sus secuencias de caracteres individuales.</TInput, TOutput> El TransformManyBlock<TInput, TOutput> objeto toma cadena valores como entrada y genera Char valores como salida.</TInput, TOutput>

         // Create a TransformManyBlock<string, char> object that splits
         // a string into its individual characters.
         var transformManyBlock = new TransformManyBlock<string, char>(
            s => s.ToCharArray());

         // Post two messages to the first block.
         transformManyBlock.Post("Hello");
         transformManyBlock.Post("World");

         // Receive all output values from the block.
         for (int i = 0; i < ("Hello" + "World").Length; i++)
         {
            Console.WriteLine(transformManyBlock.Receive());
         }

         /* Output:
            H
            e
            l
            l
            o
            W
            o
            r
            l
            d
          */

Para obtener ejemplos completos que usan TransformManyBlock<TInput, TOutput> para generar varias salidas independientes para cada entrada en una canalización de flujo de datos, vea Tutorial: crear una canalización de flujo de datos.</TInput, TOutput>

Grado de paralelismo

Cada ActionBlock<>>, TransformBlock<TInput, TOutput>, y TransformManyBlock<TInput, TOutput> mensajes de entrada de búferes del objeto hasta que el bloque está listo para procesarlos.</TInput, TOutput> </TInput, TOutput> De forma predeterminada, estas clases procesan los mensajes en el orden en el que se recibieron, un mensaje cada vez. También puede especificar el grado de paralelismo para permitir ActionBlock<>>, TransformBlock<TInput, TOutput> y TransformManyBlock<TInput, TOutput> objetos para procesar varios mensajes simultáneamente.</TInput, TOutput> </TInput, TOutput> Para obtener más información sobre la ejecución simultánea, vea la sección Especificar el grado de paralelismo más adelante en este documento. Para obtener un ejemplo que establece el grado de paralelismo para permitir que un bloque de flujo de datos de ejecución pueda procesar varios mensajes a la vez, consulte Cómo: especificar el grado de paralelismo en un Dataflow Block.

Resumen de tipos de delegado

En la tabla siguiente resume los tipos de delegado que puede proporcionar a ActionBlock<>>, TransformBlock<TInput, TOutput>, y TransformManyBlock<TInput, TOutput> objetos.</TInput, TOutput> </TInput, TOutput> En esta tabla también se especifica si el tipo de delegado funciona de forma sincrónica o asincrónica.

TipoTipo de delegado sincrónicoTipo de delegado asincrónico
ActionBlock<>>System.ActionSystem.Func<TInput, Task>
TransformBlock<TInput, TOutput></TInput, TOutput>System.Func<TInput, TOutput>2`System.Func<TInput, Task<TOutput>>
TransformManyBlock<TInput, TOutput></TInput, TOutput>System.Func<TInput, IEnumerable<TOutput>>System.Func<TInput, Task<IEnumerable<TOutput>>>

También puede utilizar expresiones lambda cuando trabaja con tipos de bloque de ejecución. Para obtener un ejemplo que muestra cómo utilizar una expresión lambda con un bloque de ejecución, consulte Cómo: realizar acción cuando un flujo de datos recibe datos del bloque.

Bloques de agrupación

Los bloques de agrupación combinan datos de uno o más orígenes y con distintas restricciones. La biblioteca de flujo de datos TPL proporciona tres tipos de bloques de combinación: BatchBlock<>>, JoinBlock<T1, T2>, y BatchedJoinBlock<T1, T2>.</T1, T2> </T1, T2>

BatchBlock(T)

El BatchBlock<> </> > clase combina los conjuntos de datos de entrada, que se conocen como lotes, en las matrices de datos de salida. Especificar el tamaño de cada lote cuando se crea un BatchBlock<> </> > objeto. Cuando el BatchBlock<> </> > objeto recibe el número especificado de elementos de entrada, propaga de forma asincrónica una matriz que contiene los elementos. Si un BatchBlock<> </> > objeto se establece en el estado completado pero no contiene elementos suficientes para formar un lote, propaga una matriz final que contiene los elementos restantes de entrada.

El BatchBlock<> </> > clase funciona en modo expansiva o no expansivo modo. En modo expansivo, que es el valor predeterminado, un BatchBlock<> </> > objeto acepta todos los mensajes que se proporciona y propaga una matriz después de recibir el número especificado de elementos. En modo no expansivo, un BatchBlock<> </> > objeto pospone todos los mensajes entrantes hasta que suficientes orígenes que proporcionen mensajes al bloque para formar un lote. Normalmente, el modo expansivo se comporta mejor que el modo no expansivo porque requiere menos sobrecarga de procesamiento. Sin embargo, se puede usar el modo no expansivo cuando se debe coordinar el consumo de varios orígenes en modo atómico. Especifica el modo no expansivo estableciendo Cuantificaciones expansivos a False en el dataflowBlockOptions parámetro en el BatchBlock<> </> > constructor.

El siguiente ejemplo básico se expone varios Int32 valores para una BatchBlock<> </> > objeto que contiene diez elementos en un lote. Para garantizar que todos los valores se propagan fuera de la BatchBlock<>>, este ejemplo llama a la Complete método. El Complete método establece la BatchBlock<> </> > objeto al estado completado y por lo tanto, la BatchBlock<> </> > objeto propaga cualquier elemento restante como un lote final.

         // Create a BatchBlock<int> object that holds ten
         // elements per batch.
         var batchBlock = new BatchBlock<int>(10);

         // Post several values to the block.
         for (int i = 0; i < 13; i++)
         {
            batchBlock.Post(i);
         }
         // Set the block to the completed state. This causes
         // the block to propagate out any any remaining
         // values as a final batch.
         batchBlock.Complete();

         // Print the sum of both batches.

         Console.WriteLine("The sum of the elements in batch 1 is {0}.",
            batchBlock.Receive().Sum());

         Console.WriteLine("The sum of the elements in batch 2 is {0}.",
            batchBlock.Receive().Sum());

         /* Output:
            The sum of the elements in batch 1 is 45.
            The sum of the elements in batch 2 is 33.
          */

Para obtener un ejemplo completo que usa BatchBlock<> </> > para mejorar la eficacia de las operaciones de inserción de base de datos, vea Tutorial: usar BatchBlock y BatchedJoinBlock para mejorar la eficacia.

JoinBlock(T1, T2, ...)

El JoinBlock<T1, T2> y JoinBlock<T1, T2, T3> clases obtienen elementos de entrada y propagan System.Tuple<T1, T2> o System.Tuple<T1, T2, T3> objetos que contienen esos elementos.</T1, T2, T3> </T1, T2> </T1, T2, T3> </T1, T2> El JoinBlock<T1, T2> y JoinBlock<T1, T2, T3> clases no heredan de ITargetBlock<>>.</T1, T2, T3> </T1, T2> En su lugar, proporcionan propiedades, Target1, Target2, y Target3, que implementan ITargetBlock<>>.

Como BatchBlock<>>, JoinBlock<T1, T2> y JoinBlock<T1, T2, T3> funcionar en modo expansivo o no expansivo.</T1, T2, T3> </T1, T2> En modo expansivo, que es el valor predeterminado, un JoinBlock<T1, T2> o JoinBlock<T1, T2, T3> objeto acepta todos los mensajes que se proporciona y propaga una tupla después de cada uno de sus destinos reciba al menos un mensaje.</T1, T2, T3> </T1, T2> En modo no expansivo, un JoinBlock<T1, T2> o JoinBlock<T1, T2, T3> objeto pospone todos los mensajes entrantes hasta que todos los destinos han proporcionado los datos que se requiere para crear una tupla.</T1, T2, T3> </T1, T2> En este punto, el bloque se involucra en un protocolo de confirmación en dos fases para recuperar atómicamente todos los elementos necesarios de los orígenes. Este aplazamiento permite que, mientras tanto, otra entidad consuma datos, para permitir que el sistema global progrese.

El siguiente ejemplo básico muestra un caso en que un JoinBlock<T1, T2, T3> objeto requiere varios datos para calcular un valor.</T1, T2, T3> Este ejemplo se crea un JoinBlock<T1, T2, T3> objeto que requiere dos Int32 valores y un Char valor para realizar una operación aritmética.</T1, T2, T3>

         // Create a JoinBlock<int, int, char> object that requires
         // two numbers and an operator.
         var joinBlock = new JoinBlock<int, int, char>();

         // Post two values to each target of the join.

         joinBlock.Target1.Post(3);
         joinBlock.Target1.Post(6);

         joinBlock.Target2.Post(5);
         joinBlock.Target2.Post(4);

         joinBlock.Target3.Post('+');
         joinBlock.Target3.Post('-');

         // Receive each group of values and apply the operator part
         // to the number parts.

         for (int i = 0; i < 2; i++)
         {
            var data = joinBlock.Receive();
            switch (data.Item3)
            {
               case '+':
                  Console.WriteLine("{0} + {1} = {2}",
                     data.Item1, data.Item2, data.Item1 + data.Item2);
                  break;
               case '-':
                  Console.WriteLine("{0} - {1} = {2}",
                     data.Item1, data.Item2, data.Item1 - data.Item2);
                  break;
               default:
                  Console.WriteLine("Unknown operator '{0}'.", data.Item3);
                  break;
            }
         }

         /* Output:
            3 + 5 = 8
            6 - 4 = 2
          */

Para obtener un ejemplo completo que usa JoinBlock<T1, T2> objetos en modo no expansivo para compartir de forma cooperativa un recurso, vea Cómo: usar JoinBlock para leer datos desde varios orígenes.</T1, T2>

BatchedJoinBlock(T1, T2, ...)

El BatchedJoinBlock<T1, T2> y BatchedJoinBlock<T1, T2, T3> clases obtienen lotes de elementos de entrada y propagan System.Tuple(IList(T1), IList(T2)) o System.Tuple(IList(T1), IList(T2), IList(T3)) objetos que contienen esos elementos.</T1, T2, T3> </T1, T2> Piense en BatchedJoinBlock<T1, T2> como una combinación de BatchBlock<> </> > y JoinBlock<T1, T2>.</T1, T2> </T1, T2> Especificar el tamaño de cada lote cuando se crea un BatchedJoinBlock<T1, T2> objeto.</T1, T2> BatchedJoinBlock<T1, T2> también proporciona propiedades, Target1 y Target2, que implementan ITargetBlock<>>.</T1, T2> Cuando se recibe el número especificado de elementos de entrada a través de todos los destinos, el BatchedJoinBlock<T1, T2> objeto propaga de forma asincrónica un System.Tuple(IList(T1), IList(T2)) objeto que contiene esos elementos.</T1, T2>

El siguiente ejemplo básico se crea un BatchedJoinBlock<T1, T2> objeto que contiene los resultados, Int32 valores y errores que son excepción objetos.</T1, T2> En este ejemplo se realizan varias operaciones y se escriben los resultados en la Target1 propiedad y los errores a la Target2 propiedad, de la BatchedJoinBlock<T1, T2> objeto.</T1, T2> Dado que el recuento de operaciones correctas e incorrectas se conoce de antemano, el IList<> </> > objetos permiten que cada destino reciba cero o más valores.

         // For demonstration, create a Func<int, int> that 
         // returns its argument, or throws ArgumentOutOfRangeException
         // if the argument is less than zero.
         Func<int, int> DoWork = n =>
         {
            if (n < 0)
               throw new ArgumentOutOfRangeException();
            return n;
         };

         // Create a BatchedJoinBlock<int, Exception> object that holds 
         // seven elements per batch.
         var batchedJoinBlock = new BatchedJoinBlock<int, Exception>(7);

         // Post several items to the block.
         foreach (int i in new int[] { 5, 6, -7, -22, 13, 55, 0 })
         {
            try
            {
               // Post the result of the worker to the 
               // first target of the block.
               batchedJoinBlock.Target1.Post(DoWork(i));
            }
            catch (ArgumentOutOfRangeException e) 
            {
               // If an error occurred, post the Exception to the 
               // second target of the block.
               batchedJoinBlock.Target2.Post(e); 
            }
         }
        
         // Read the results from the block.
         var results = batchedJoinBlock.Receive();

         // Print the results to the console.

         // Print the results.
         foreach (int n in results.Item1)
         {
            Console.WriteLine(n);
         }
         // Print failures.
         foreach (Exception e in results.Item2)
         {
            Console.WriteLine(e.Message);
         }

         /* Output:
            5
            6
            13
            55
            0
            Specified argument was out of the range of valid values.
            Specified argument was out of the range of valid values.
          */

Para obtener un ejemplo completo que usa BatchedJoinBlock<T1, T2> para capturar los resultados y las excepciones que se producen mientras el programa lee de una base de datos, vea Tutorial: usar BatchBlock y BatchedJoinBlock para mejorar la eficacia.</T1, T2>

[go to top]

Puede habilitar opciones adicionales si proporciona un System.Threading.Tasks.Dataflow.DataflowBlockOptions objeto al constructor de tipos de bloques de flujo de datos. Estas opciones controlan el comportamiento, como el del programador que administra la tarea subyacente, y el grado de paralelismo. El DataflowBlockOptions también tiene tipos derivados que especifican el comportamiento es específico de ciertos tipos de bloques de flujo de datos. En la tabla siguiente se resumen los tipo de opciones que se asocian a cada tipo de bloques de flujo de datos.

Tipo de bloques de flujo de datosDataflowBlockOptions tipo
BufferBlock<>>DataflowBlockOptions
BroadcastBlock<>>DataflowBlockOptions
WriteOnceBlock<>>DataflowBlockOptions
ActionBlock<>>ExecutionDataflowBlockOptions
TransformBlock<TInput, TOutput></TInput, TOutput>ExecutionDataflowBlockOptions
TransformManyBlock<TInput, TOutput></TInput, TOutput>ExecutionDataflowBlockOptions
BatchBlock<>>GroupingDataflowBlockOptions
JoinBlock<T1, T2></T1, T2>GroupingDataflowBlockOptions
BatchedJoinBlock<T1, T2></T1, T2>GroupingDataflowBlockOptions

Las secciones siguientes proporcionan información adicional acerca de los tipos de flujo de datos importantes de opciones de bloque que están disponibles a través de la System.Threading.Tasks.Dataflow.DataflowBlockOptions, System.Threading.Tasks.Dataflow.ExecutionDataflowBlockOptions, y System.Threading.Tasks.Dataflow.GroupingDataflowBlockOptions clases.

Especificar el programador de tareas

Cada bloque de flujo de datos predefinido utiliza el mecanismo de programación de tareas de la biblioteca TPL para realizar actividades como propagar datos a un destino, recibir datos de un origen y ejecutar delegados definido por el usuario si los datos están disponibles. TaskScheduler es una clase abstracta que representa un programador de tareas que pone en cola tareas en los subprocesos. El programador de tareas predeterminado, predeterminado, usa el ThreadPool clase poner en cola y ejecutar el trabajo. Puede reemplazar el programador de tareas predeterminado estableciendo la TaskScheduler propiedad cuando se construye un objeto de bloque de flujo de datos.

Cuando el mismo programador de tareas administra varios bloques de flujo de datos, puede aplicar directivas entre ellos. Por ejemplo, si varios bloques de flujo de datos están configurados para el destino del programador exclusivo del mismo ConcurrentExclusiveSchedulerPair objeto, todo el trabajo que se ejecuta a través de estos bloques se serializa. De forma similar, si estos bloques se configuran como destino del programador simultáneo del mismo ConcurrentExclusiveSchedulerPair objeto y el programador está configurada para tener un nivel de simultaneidad máximo, todo el trabajo de estos bloques se limita a ese número de operaciones simultáneas. Para obtener un ejemplo que utiliza la ConcurrentExclusiveSchedulerPair clase para permitir operaciones de lectura en paralelo, pero escribir las operaciones que ocurren exclusivamente en todas las operaciones, consulte Cómo: especificar un programador de tareas en un Dataflow Block. Para obtener más información acerca de los programadores de tareas en la biblioteca TPL, vea la TaskScheduler tema de la clase.

Especificar el grado de Paralelismo

De forma predeterminada, los tipos de bloques de tres ejecución que proporciona la biblioteca de TPL Dataflow, ActionBlock<>>, TransformBlock<TInput, TOutput>, y TransformManyBlock<TInput, TOutput>, procesar un mensaje a la vez.</TInput, TOutput> </TInput, TOutput> Estos tipos de bloques de flujo de datos también procesan mensajes en el orden en que se reciben. Para habilitar estos bloques de flujo de datos procesar mensajes de forma simultánea, establezca el ExecutionDataflowBlockOptions.MaxDegreeOfParallelism propiedad cuando se construye el objeto de bloques de flujo de datos.

El valor predeterminado de MaxDegreeOfParallelism es 1, lo que garantiza que el bloque procesa un mensaje a la vez. Al establecer esta propiedad en un valor mayor de 1 se permite que el bloque de flujo de datos procese varios mensajes simultáneamente. Establecer esta propiedad en DataflowBlockOptions.Unbounded permite que el programador de tareas subyacente administre el grado máximo de simultaneidad.

System_CAPS_ICON_important.jpg Importante

Cuando se especifica un grado máximo de paralelismo mayor que 1, varios mensajes se procesan simultáneamente y, por consiguiente, los mensajes no se pueden procesar en el orden en que se reciben. El orden en que los mensajes se generan desde el bloque, sin embargo, se ordenarán correctamente.

Dado que la MaxDegreeOfParallelism propiedad representa el grado máximo de paralelismo, el bloque puede ejecutarse con un menor grado de paralelismo que el especificado. El bloque de flujo de datos puede utilizar un menor grado de paralelismo para cumplir los requisitos funcionales o porque hay una falta de recursos del sistema disponibles. Un flujo de datos bloqueado nunca elige más paralelismo que el especificado.

El valor de la MaxDegreeOfParallelism propiedad es exclusivo para cada objeto de bloque de flujo de datos. Por ejemplo, si cuatro objetos de bloques de flujo de datos especifican 1 como el grado máximo de paralelismo, los cuatro objetos de bloques de flujo de datos podrían ejecutarse en paralelo.

Para obtener un ejemplo que establece el grado máximo de paralelismo para permitir operaciones largas se produzcan en paralelo, vea Cómo: especificar el grado de paralelismo en un Dataflow Block.

Especificar el número de mensajes por tarea

Los tipos predefinidos de bloques de flujo de datos utilizan tareas para procesar varios elementos de entrada. Esto ayuda a minimizar el número de objetos de tarea necesarios para procesar datos, lo que permite que las aplicaciones se ejecuten más eficazmente. Sin embargo, cuando las tareas de un conjunto de bloques de flujo de datos están procesando datos, es posible que las tareas de otros bloques de flujo de datos tengan que esperar el tiempo de procesamiento en la cola mensajes. Para habilitar una mejor equidad entre tareas de flujo de datos, establezca la MaxMessagesPerTask propiedad. Cuando MaxMessagesPerTask está establecido en DataflowBlockOptions.Unbounded, que es el valor predeterminado, la tarea utilizada por un bloque de flujo de datos procesa tantos mensajes como están disponibles. Cuando MaxMessagesPerTask se establece en un valor distinto de Unbounded, el bloque procesa como máximo este número de mensajes por tarea objeto. Aunque al establecer el MaxMessagesPerTask propiedad puede aumentar la equidad entre tareas, puede provocar que el sistema cree más tareas que son necesarios, lo que puede disminuir el rendimiento.

Habilitar la cancelación

La biblioteca TPL proporciona un mecanismo que habilita las tareas para coordinar la cancelación de manera cooperativa. Para habilitar los bloques de flujo de datos puedan participar en este mecanismo de cancelación, establezca el CancellationToken propiedad. Cuando esto CancellationToken objeto se establece en el estado cancelado, todos los bloques de flujo de datos que controlan este token finalizan la ejecución de su elemento actual pero no comienzan a procesar los elementos siguientes. Estos bloques de flujo de datos también borran los mensajes almacenados en búfer, conexiones de inicio para los bloques de origen y de destino, y la transición al estado cancelado. Al realizar la transición al estado cancelado, el finalización propiedad tiene la estado propiedad establecida en cancelado, a menos que se produjo una excepción durante el procesamiento. En ese caso, estado está establecido en error.

Para obtener un ejemplo que muestra cómo usar la cancelación en una aplicación de formularios Windows Forms, vea Cómo: cancelar un Dataflow Block. Para obtener más información sobre la cancelación en la biblioteca TPL, vea cancelación de tareas.

Especificar el comportamiento expansivo frente al no expansivo

Varios tipos de bloques de flujo de datos de agrupación pueden trabajar en modo expansiva o no expansivo modo. De forma predeterminada, los tipos de bloques de flujo de datos predefinidos funcionan en modo expansivo.

Para la combinación de tipos de bloques como JoinBlock<T1, T2>, el modo expansivo significa que el bloque acepta datos inmediatamente aunque los correspondientes datos con el que se va a unir no están disponibles.</T1, T2> El modo no expansivo significa que el bloque pospone todos los mensajes entrantes hasta que uno esté disponible para cada uno de sus destinos para completar la combinación. Si los mensajes pospuestos ya no están disponibles, el bloque de combinación libera todos los mensajes pospuestos y reinicia el proceso. Para el BatchBlock<> </> > clase expansiva y comportamiento no expansivo es similar, salvo que en el modo no expansivo, un BatchBlock<> </> > objeto pospone todos los mensajes entrantes hasta que haya suficientes disponibles de orígenes distintos para completar un lote.

Para especificar el modo no expansivo para un bloque de flujo de datos, establezca Cuantificaciones expansivos a False. Para obtener un ejemplo que muestra cómo utilizar el modo no expansivo para habilitar varios bloques de combinación puedan compartir un origen de datos más eficazmente, consulte Cómo: usar JoinBlock para leer datos desde varios orígenes.

[go to top]

Aunque la biblioteca de flujos de datos TPL proporciona muchos tipos de bloques predefinidos, puede crear tipos de bloques adicionales que tengan un comportamiento personalizado. Implemente el ISourceBlock<> </> > o ITargetBlock<> </> > interfaces directamente o utilizar el encapsular<TInput, TOutput> método para crear un bloque complejo que encapsule el comportamiento de los tipos de bloques existentes.</TInput, TOutput> Para obtener ejemplos que muestran cómo implementar funcionalidad en bloques de flujo de datos personalizados, consulte Tutorial: crear un tipo de bloque de flujo de datos personalizado.

[go to top]

TítuloDescripción
Cómo: escribir y leer los mensajes de un bloque de flujo de datosMuestra cómo escribir y leer los mensajes de una BufferBlock<> </> > objeto.
Cómo: implementar un modelo de flujo de datos productor-consumidorDescribe cómo utilizar el modelo de flujo de datos para implementar un patrón consumidor-productor, cuando el productor envía mensajes a un bloque de flujo de datos y el consumidor lee mensajes de ese bloque.
Cómo: realizar una acción cuando un bloque de flujo de datos recibe datosDescribe cómo proporcionar delegados a los tipos de bloques de flujo de datos de ejecución, ActionBlock<>>, TransformBlock<TInput, TOutput>, y TransformManyBlock<TInput, TOutput>.</TInput, TOutput> </TInput, TOutput>
Tutorial: Crear una canalización de flujo de datosDescribe cómo crear una canalización de flujo de datos que descarga texto desde Internet y realiza operaciones en ese texto.
Cómo: desvincular bloques de flujo de datosMuestra cómo utilizar el enlacesa método desvincular un bloque de destino de su origen después de que el origen de un mensaje al destino.
Tutorial: Usar el flujo de datos en una aplicación de Windows FormsMuestra cómo crear una red de bloques de flujo de datos que realizan procesamiento de imágenes en una aplicación de Windows Forms.
Cómo: cancelar un bloque de flujo de datosMuestra cómo se usa la cancelación en una aplicación de Windows Forms.
Cómo: usar JoinBlock para leer los datos de varios orígenesExplica cómo utilizar el JoinBlock<T1, T2> clase para realizar una operación cuando hay datos de varios orígenes y cómo utilizar el modo no expansivo para habilitar varios bloques de combinación puedan compartir un origen de datos más eficazmente.</T1, T2>
Cómo: especificar el grado de paralelismo en un bloque de flujo de datosDescribe cómo establecer el MaxDegreeOfParallelism propiedad para habilitar un bloque de flujo de datos de ejecución pueda procesar varios mensajes a la vez.
Cómo: especificar un programador de tareas en un bloque de flujo de datosMuestra cómo asociar un programador de tareas específico cuando se usa flujo de datos en la aplicación.
Tutorial: Usar BatchBlock y BatchedJoinBlock para mejorar la eficaciaDescribe cómo utilizar el BatchBlock<> </> > clase para mejorar la eficacia de la base de datos insertar operaciones y cómo utilizar el BatchedJoinBlock<T1, T2> clase para capturar los resultados y las excepciones que se producen mientras el programa lee de una base de datos.</T1, T2>
Tutorial: Crear un tipo de bloque de flujo de datos personalizadosMuestra dos maneras de crear un tipo de bloque de flujo de datos que implementa un comportamiento personalizado.
Task Parallel Library (TPL)Presenta la biblioteca TPL, una biblioteca que simplifica la programación paralela y simultánea en aplicaciones de .NET Framework.
Mostrar: