Using producers and consumers - C#. NET

The examples in this section cover decoupling the Console class and pipelining functions.

Decoupling the Console Class
Mentioned that you should avoid using the System.Console class in parallel programs. The synchronization in the Console class affects the overall performance of your application, and adding Console.WriteLine() calls when trying to fix a problem can actually change the interaction between the tasks in your program enough that you may not be able to recreate the issue.

That said, everyone still uses System.Console —myself included. And, having admitted and accepted that fact, the best compromise is to use a decoupled console class, which is a simple Producer/Consumer pattern that allows Tasks to put messages destined for the console into a queue with minimal delay and for a single consumer Task to take those messages and write them out.

The Code
The following implementation is a little more general than it needs to be. The items in the queue are System.Actions that write messages to the Console class. You could easily adapt this model to perform other types of Action as required.

Using the Code
Using the decoupled console is just like using the regular Console class. The following code starts a number of Tasks, each of which writes a number of messages:

Creating a Pipeline
A pipeline chains together a number of functions that are used to process data values. The result of one function is used as the input to the next in the pipeline—the result of the final function is the result of the pipeline as a whole.

You could simple write one function that encompassed all of the transformations represented by the individual functions, but the benefit of using a pipeline like the one included here is flexibility. You can easily change the functions that are used, change the order in which they are applied, and reuse the basic logic as required.

The Code
This version is a decoupled, long-lived pipeline. Long-lived meaning that once we have created an instance and started it off, we can feed values in over a period of time, and those values will be processed using the chained functions. Decoupled means that when we add a value, we also supply a callback that will be invoked when the value has been processed by all of the functions—the AddValue() method doesn’t block while the functions are applied.

We create a new instance by providing the first function you want in the pipeline. Additional functions are added by calling the AddFunction() method. This implementation is strongly typed, such that when we add a new function to the pipeline, the input type must match the output type of the last function we added.

Once we have added all of the functions we require, we must call the StartProcessing() method. This creates the underlying collection and the Task and Parallel.ForEach loop that collect and push values through the function chain. We close down the pipeline by calling the StopProcessing() method.

Using the Code
The following code defines three very simple functions and chains them together with a Pipeline. Sequential values are generated and put into the pipeline, along with a callback that filters the results and prints out selected values to the Console.


All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

C#. NET Topics