Other Posts in Multithreading

  1. Killing a Process through C#
  2. Multithreading in C#

Multithreading in C#

10/7/2009

Multithreading seems to be a hot topic of discussion for a lot of programmers. It also seems to be one of the more difficult concepts to grasp (or even to get started with). Luckily in C#/.Net it's pretty simple when compared to some other languages that I've dealt with. In C# all you need to do is make a call to ThreadPool or create a Thread class and you're done... Sort of anyway... However there is a lack of certain functionality that you may want to add. In my case I needed a way of being notified of certain events so I created a helper base class that is reusable.

   1: /// <summary>
   2: /// Worker class used in multi threading
   3: /// </summary>
   4: /// <typeparam name="ResultType">Result type</typeparam>
   5: /// <typeparam name="InputParams">The input parameter type</typeparam>
   6: public abstract class Worker<ResultType,InputParams>
   7: {
   8:     #region Constructor
   9:  
  10:     /// <summary>
  11:     /// Constructor
  12:     /// </summary>
  13:     /// <param name="Params">Parameters used in the function</param>
  14:     protected Worker(InputParams Params)
  15:     {
  16:         this.Params = Params;
  17:         this.WorkerThread = new Thread(DoWork);
  18:     }
  19:  
  20:     #endregion
  21:  
  22:     #region Protected Abstract Functions
  23:  
  24:     /// <summary>
  25:     /// Function that actually does the work
  26:     /// </summary>
  27:     /// <param name="Params">Parameter used by the function</param>
  28:     /// <returns>The result of the function</returns>
  29:     protected abstract ResultType Work(InputParams Params);
  30:  
  31:     #endregion
  32:  
  33:     #region Public Functions
  34:  
  35:     /// <summary>
  36:     /// Starts the thread
  37:     /// </summary>
  38:     public void Start()
  39:     {
  40:         this.WorkerThread.Start();
  41:     }
  42:  
  43:     /// <summary>
  44:     /// Stops the thread and waits for it to finish
  45:     /// </summary>
  46:     public void Stop()
  47:     {
  48:         if (WorkerThread != null && WorkerThread.IsAlive)
  49:         {
  50:             this.WorkerThread.Join();
  51:             this.WorkerThread = null;
  52:         }
  53:         OnEndEventArgs EndEvents = new OnEndEventArgs();
  54:         EndEvents.Content = Result;
  55:         EventHelper.Raise<OnEndEventArgs>(Finished, this, EndEvents);
  56:     }
  57:  
  58:     #endregion
  59:  
  60:     #region Private Functions
  61:  
  62:     /// <summary>
  63:     /// Wrapper for the function that actually does the work.
  64:     /// Calls the start and finished events as well as stores
  65:     /// the result for use within the class.
  66:     /// </summary>
  67:     private void DoWork()
  68:     {
  69:         EventHelper.Raise<OnStartEventArgs>(Started, this, new OnStartEventArgs());
  70:  
  71:         Result = Work(this.Params);
  72:  
  73:         OnEndEventArgs EndEvents = new OnEndEventArgs();
  74:         EndEvents.Content = Result;
  75:         EventHelper.Raise<OnEndEventArgs>(Finished, this, EndEvents);
  76:     }
  77:  
  78:     #endregion
  79:  
  80:     #region Events
  81:  
  82:     /// <summary>
  83:     /// Called when the thread is finished
  84:     /// </summary>
  85:     public EventHandler<OnEndEventArgs> Finished { get; set; }
  86:  
  87:     /// <summary>
  88:     /// Called when the thread is started
  89:     /// </summary>
  90:     public EventHandler<OnStartEventArgs> Started { get; set; }
  91:  
  92:     /// <summary>
  93:     /// Can be used by the worker function to indicate progress
  94:     /// </summary>
  95:     public EventHandler<ChangedEventArgs> Updated { get; set; }
  96:  
  97:     /// <summary>
  98:     /// Can be used by the worker function to indicate an exception has occurred
  99:     /// </summary>
 100:     public EventHandler<OnErrorEventArgs> Exception { get; set; }
 101:  
 102:     #endregion
 103:  
 104:     #region Properties
 105:  
 106:     /// <summary>
 107:     /// Indicates whether or not the thread is stopped/started
 108:     /// </summary>
 109:     public bool Stopped 
 110:     { 
 111:         get 
 112:         { 
 113:             if (WorkerThread != null && WorkerThread.IsAlive)
 114:                 return false;
 115:             return true; 
 116:         }
 117:     }
 118:  
 119:     /// <summary>
 120:     /// The result (can be used by the class that inherits from this base class
 121:     /// </summary>
 122:     protected ResultType Result { get; set; }
 123:  
 124:     /// <summary>
 125:     /// Parameters used in the function
 126:     /// </summary>
 127:     private InputParams Params = default(InputParams);
 128:  
 129:     /// <summary>
 130:     /// The thread used
 131:     /// </summary>
 132:     private Thread WorkerThread=null;
 133:  
 134:     #endregion
 135: }

The code should be fairly easy to follow. You use this as a base class adding the result and parameter type. There is one function that you need to override. That's it really, well with one exception. The class has events that it uses (which uses EventArgs that are within my utility library). It has Started and Finished which is called when the thread is started and stopped respectively. The Finished event will contain the results from the function within the Content object.There are also events for Updated and Exception which are optional. The Updated event would be used to update the rest of the world on the thread's progress. The Exception event on the other hand would be used when the thread has an exception, letting everyone else know. Anyway, I hope that the code can be of use to someone. So try it out, leave feedback, and happy coding.



Comments