Objective
This tip is a quick "get up and running" introduction to parallel programming using a working example rather than any theory.
Example Origin
When I adopted the message handler approach to distributing ticket data between busses and their control office, my initial instinct was that this tailor made for a parallel solution with each datastore on its own thread. It quickly occured to me that this was a sure and certain way to corrupt my database. e.g the row to record a ticket validation could be created before the row to record the sale of the same ticket if its thread got priority!
However as time moved on a scenario has evolved that is ideally suited to parallel programming. When a customer with a series of unpaid tickets makes a payment to cover the outstanding amount, this payment is initially recorded in sequence with any other associated data, e.g. a preceeding sale. Then I need to kick off a task which writes a contra entry to the account for the funds of the to be redistributed, and scans the purchase list for that customer beginning with the oldest unpaid ticket and writes an entry to pay each off until either the funds are used up or that customer has no more tickets with money owed on them. There is no technical database dependency on this task, and if run sequentially could present a performance bottleneck.
Using the Sample Code
I used Listing 2-4 of Adam Freeman's book "Pro .NET 4 Parallel Programming in C# as my template.
I started with a standard C# console application, and included the System.Threading namespace. I renamed the application's autogenerated class from "Program" to "ParallelTest". Then I added a static integer method, "Timed_Message" to take a message string and an interval as arguments. This method loops 10 times and sends text to the console at intervals based on the interval argument supplied.
The main method spawns two tasks each calling "Timed_Message", but supplying different text and interval values for each instance. There is a final Console.Readline to prevent the main thread from concluding and killing off the sub threads before they get the opportunity to show how they work together. This is the code:
<pre>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace ParallelTest
{
class ParallelTest
{
private static int Timed_Message(String arg_Message, int arg_Interval)
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Source {0} - Cycle {1} for Interval {2}", arg_Message, i, arg_Interval);
Thread.Sleep(1000 * arg_Interval);
}
Console.WriteLine("{0} - Complete", arg_Message);
return 0;
}
static void Main(string[] args)
{
int RetCode = 0;
Task RedistributionTask = new Task(() => RetCode = Timed_Message("Five ", 4));
RedistributionTask.Start();
Task AltRedistributionTask = new Task(() => RetCode = Timed_Message("Three ", 2));
AltRedistributionTask.Start();
//Timed_Message("Main", 6);
// wait for input before exiting
Console.WriteLine("Press enter to finish after both [Complete] messages appear.");
Console.ReadLine();
}
}
}
</pre>
Press enter to finish after both [Complete] messages appear.
Source Five - Cycle 0 for Interval 4
Source Three - Cycle 0 for Interval 2
Source Three - Cycle 1 for Interval 2
Source Five - Cycle 1 for Interval 4
Source Three - Cycle 2 for Interval 2
Source Three - Cycle 3 for Interval 2
Source Five - Cycle 2 for Interval 4
Source Three - Cycle 4 for Interval 2
Source Three - Cycle 5 for Interval 2
Source Five - Cycle 3 for Interval 4
Source Three - Cycle 6 for Interval 2
Source Three - Cycle 7 for Interval 2
Source Five - Cycle 4 for Interval 4
Source Three - Cycle 8 for Interval 2
Source Three - Cycle 9 for Interval 2
Source Five - Cycle 5 for Interval 4
Three - Complete
Source Five - Cycle 6 for Interval 4
Source Five - Cycle 7 for Interval 4
Source Five - Cycle 8 for Interval 4
Source Five - Cycle 9 for Interval 4
Five - Complete
Try this at home.
Uncomment the line Timed_Message("Main", 6); in the main method and re run the application. Now you will see the main thread running in parallel with the other two, and because we have used a longer interval, it continues working after the other two are done. This is an extract from the revised output :
Source Main - Cycle 3 for Interval 6
Source Three - Cycle 9 for Interval 2
Source Five - Cycle 5 for Interval 4
Three - Complete
Source Main - Cycle 4 for Interval 6
Source Five - Cycle 6 for Interval 4
Source Five - Cycle 7 for Interval 4
Source Main - Cycle 5 for Interval 6
Source Five - Cycle 8 for Interval 4
Source Main - Cycle 6 for Interval 6
Source Five - Cycle 9 for Interval 4
Five - Complete
Source Main - Cycle 7 for Interval 6
Source Main - Cycle 8 for Interval 6
Source Main - Cycle 9 for Interval 6
Main - Complete
Press enter to finish after both [Complete] messages appear.
History
22-Jan-2014 1.0 Initial post
22-Jan-2014 1.1 Code reformatted