.NET Parallel Programming with Events

Purpose

 
Many times the Application needs to be notified about what is happening inside a Parallel Task and also to get data (if any) from the Task in real time. For this, Events can be used with Tasks.
 
Implementation Details
 
Consider a Task that does the countdown starting from a user specified number. The Application wants to know each time a countdown has been done so that it can print the value to the UI. This can be done via Events.
 
The Class which does the Count Down is the CountDownDriver class. 
 
Parallel1.gif
 
The class exports 2 custom events.
  1. #region Custom Event Delegates  
  2. public delegate void CountDown(object o, EventArgs e);  
  3. public delegate void CountDownComplete(object o, EventArgs e);  
  4. #endregion  
  5.  
  6. #region Custom Events  
  7. public event CountDown OnCountDown;  
  8. public event CountDownComplete OnCountDownComplete;  
  9. #endregion 
The OnCountDown event is fired by Parallel Task after each count down. The OnCountDownComplete event is fired after the countdown is complete.
  1. public void StartCountDown()  
  2. {  
  3.     for (int i = StartNo; i >= 0; i--)  
  4.     {  
  5.         System.Threading.Thread.Sleep(1000);  
  6.   
  7.         if (i == 0)  
  8.         {  
  9.             if (OnCountDownComplete != null)  
  10.             {  
  11.                 //Fire OnCountDownComplete event  
  12.                 OnCountDownComplete(thisnew EventArgs());  
  13.             }  
  14.   
  15.             break;  
  16.         }  
  17.   
  18.         if (OnCountDown != null)  
  19.         {  
  20.             this.CurrentCountDown = i;  
  21.             //Fire OnCountDown event  
  22.             OnCountDown(thisnew EventArgs());  
  23.         }  
  24.   
  25.     }  
When the Application launches the Tasks, these Events are mapped to Event Handlers in the Application.
  1. NoOfParallelTasks = int.Parse(tbNoOfParallelTasks.Text);  
  2.   
  3. for (int i = 0; i < NoOfParallelTasks; i++)  
  4. {  
  5.     CountDownDriver countDown = new CountDownDriver { StartNo = int.Parse(tbStartNo.Text), TaskNo = i + 1 };  
  6.   
  7.     countDown.OnCountDown += new CountDownDriver.CountDown(countDown_OnCountDown);  
  8.     countDown.OnCountDownComplete += new CountDownDriver.CountDownComplete(countDown_OnCountDownComplete);  
  9.   
  10.     //Start Parallel Task  
  11.     System.Threading.Tasks.Task.Factory.StartNew(new Action(() => countDown.StartCountDown()));  
Thus, when these Events are fired inside the Parallel Tasks, the Application is notified and the Event Handler executes. The data from the Parallel Task is a parameter of the Event and can be accessed inside the Event Handler. In this demo, the code in the Event Handlers prints data from the Parallel Task to the UI.
  1. void countDown_OnCountDown(object o, EventArgs e) {  
  2.     lock(this) {  
  3.         CountDownDriver countDown = (CountDownDriver) o;  
  4.         rtbResult.Invoke(() => {  
  5.             this.rtbResult.Text = this.rtbResult.Text + "\n" +  
  6.                 "Countdown from parallel task " +  
  7.                 countDown.TaskNo.ToString() + " : " +  
  8.                 countDown.CurrentCountDown.ToString();  
  9.         });  
  10.     }  
Note: Invoke has to be used to acquire the UI Thread first before writing to it. I have written an Invoke() Extension Method to Control which accepts an Action (Lambda Expression) parameter as shown below :
  1. public static void Invoke(this Control control, System.Action action)  
  2. {  
  3.     if (control.InvokeRequired)  
  4.     {  
  5.         control.Invoke(action);  
  6.     }  
  7.     else  
  8.     {  
  9.         action();  
  10.     }  
  11. }  
Parallel2.gif
 
2 Parallel Tasks are started to count down from 5 in the sample above.