The TPL Dataflow Library (TDF) builds on the TPL with features that enable you to build services that can produce and consume data asynchronously. The TDF is based around a set of datatypes that can act as sources and sinks of data, and linkages that connect sources to sinks.
The simplest sink is provided by the generic ActionBlock type. This class implements a method named Post that receives data. You can specify the processing to be performed for each item received by associating a delegate with the ActionBlock object. The following example shows how to create an ActionBlock object that passes all integer data it receives to the method named ProcessData.
The ActionBlock class is an example of an executor block other executor block types are available, such as the TransformBlock and the TransformManyBlock classes. The TDF also includes a number of classes that simply implement data buffering; you can retrieve and process each item individually. A prime example is the generic BufferBlock class illustrated in the following example.
A useful variation on the BufferBlock type is the BroadcastBlock class. This buffer class enables you to link to multiple executor blocks simultaneously. Each item of data is passed to every linked executor block. You also provide a delegate to a cloning method that specifies any transformations to apply to the data before sending it to each executor block.
As a complete worked example, the following WPF application captures data generated by an event source and plots this data on a graph (the graphCanvas element that occupies the major part of the window). The user clicks the Plot Graph button to initiate the event source and start collecting data. The data is also logged to a file, just to show how to use a BroadcastBlock object to direct data to multiple destinations.
The following listing shows the complete code for the event source, implemented by the EventDataSource class. This class raises a DataPointGenerated event for each datum. The data itself consists of Point objects. This data is generated by the GenerateData method, which slowly performs a complex trigonometric calculation (the actual function is immaterial, but this method produces data which results in a rather pretty graph).
Finally, the following code shows the application logic behind the GraphWindow window.
The plotButton_Click method runs when the user clicks the Plot Button button. This method creates an instance of the BroadcastBlock class; the cloning method specified simply copies the data as-is to each executor block.
The graphPlotter ActionBlock executor takes each Point object passed to it and invokes the plotPoint method to plot the corresponding point on the graphCanvas element of the WPF window. Note that the Task created to actually run the plotPoint method must execute on the UI thread, so the ExecutiondataflowBlockOptions object provided to the ActionBlock constructor specifies that it should utilize the task scheduler for the user interface synchronization context. The dataLogger ActionBlock executor logs the same Point data to a file (the code is omitted from the example). In this case, the Task created to handle this data should not run on the UI thread, so the code does not specify any scheduler options allowing the TDF to employ its default scheduling semantics.
Both ActionBlock executors are attached to the pointBroadcaster buffer block by using the LinkTo method, before the code arranges to capture DataPointGenerated events form the event source and post them to the pointBroadcaster object. Finally, the code starts the event source running asynchronously.
When you start the application and click Plot Button, the application slowly displays the graph in the WPF window (the event source raises a pair of events for each data point every 10 milliseconds, but there are a large number of data points). However, the user interface is still responsive (you can move the window around) because the plotPoint and logPoint methods are executed asynchronously.
The TDF is a highly extensible collection of types for implementing asynchronous data processing based on the producer/consumer model. This article has shown some of the basic features, but there are several other executor and buffer classes available. Furthermore, you can define your own custom executor and buffer classes by implementing the ISourceBlock, ITargetBlock, and IPropagatorBlock interfaces of the TDF. Visit the Introduction to TPL Dataflow page on the Microsoft Web site to download the library and documentation.