Multi-threading in C Sharp (C#)

Threading is fun, because with it you can do a lot more stuff at the same time. For example, keeping your UI updated while your background tasks are running.

C# supports parallel execution of code through multi-threading. A thread is an independent execution path, able to run simultaneously with other threads. Here, we examine three simple C# approaches to help your application perform multiple tasks simultaneously.

  1. Method 1: Using System.Thread (easy but unsafe, for low risk use)
  2. Method 2: Using Application.DoEvents (okay for general purposes)
  3. Method 3: Using BackgroundWorker class (preferred for thread safety)

Of the three methods listed above, the method 1 is the easiest, but is considered unsafe. However, I still find it useful as a quick-n-dirty implementation for rapid prototyping developing, running simulations or testing out new algorithms. It also provides for simple pause and resume mechanisms.

Download: Demo using System.Threading.

Method 2 is an event-driven approach that applies asynchronous event handlers, and then calls Application.DoEvents to repaint the UI. Hence, its more like multi-tasking rather than multi-threading. In addition, it has a slight re-entry flaw supposed inherited from its predecessors.

Download: Demo using Application.DoEvents.

For now, it seems like method 3 is the generally accepted preferred practice using thread-safe methods. Its a bit more tedious to implement neatly. I have included source code to provide a short framework to illustrate its capabilities.

Download: Demo using BackgroundWorker.

Method 1: Using System.Threading (easy)

Using System.Threading for threading purposes is bad. The class exposes two dangerous methods Suspend() and Resume() that are considered unsafe, and are given deprecated warnings since .NET 2.0. However, I still find it useful as a quick-n-dirty implementation for rapid prototyping developing, running simulations or testing out new algorithms. It also provides for simple pause and resume mechanisms. Simplest approach if you need to show a prototype to your client quickly.

Nevertheless, its important to understand that its major problem is that of deadlocks. You have no way of knowing what code a thread is executing when you suspend it. If you suspend a thread while it holds locks during a security permission evaluation, other threads in the AppDomain might be blocked. If you suspend a thread while it is executing a class constructor, other threads in the AppDomain that attempt to use that class are blocked. Deadlocks can occur very easily. Here’s an example of a program exhibiting deadlock:

So at any point in time, the thread could be doing anything. Executing suspend stops the thread running in its tracks. Suspended. Hence, imagine your thread is reading a file and places a lock on it. You suspend your thread. File stays locked. Same goes for any other resources.

Anyway, if you need a quick solution, here is an outline of how the demo source above works. The UI object starts a new thread for the task object. Hence the UI object and the task object run on separate threads. The task object then performs the endless loop. On each iteration, the task object has to perform an invoke to update the UI object on a cross-thread. Thats it, easy.

Method 2: Using Application.DoEvents (okay)

To start, lets quickly run through what Application.DoEvents() actually does. From the framework, each time the form handles an event, it processes all the code associated with that event. All other events wait in a queue. While your code handles the event, your application does not respond. For example, the window does not repaint if another window is dragged on top.

If you call DoEvents in your code, your application will somewhat interrupt your code and handle the other events. For example, if you have a form that adds data to a ListBox and add DoEvents to your code, your form repaints when another window is dragged over it. If you remove DoEvents from your code, your form will not repaint until the click event handler of the button is finished executing.

With reference to the demo source code above, we first create an EventArgs object that basically stores the UI update data. So this EventArgs dutifully carries data from the task object to the UI object in each iteration.

Before the UI starts running the task, it pre-determines the updates that need that to be done by adding a new event to the task through AddEndCycleEvent. Note that Application.DoEvent is supposed to be called at the end the pre-determined set of updates to redraw the UI.

Finally, at the end of every iteration in the task object, it calls OnEndCycle(), which sends the EventArgs object back to the UI through the EventHandler.

Again, this method is very simple to use. But here’s the catch. The DoEvent method has a re-entry problem: Calling Application.DoEvents can cause code to be re-entered or re-performed if a message raises an event. In other words, this approach is not exactly multi-threading, but rather multi-tasking by switching to process event to event in the queue. Rightfully, long running tasks should be running in another thread, then we marshal calls to update the UI back to the UI thread. So DoEvents is considered a hacky solution to this problem because:

Application.DoEvents is taken straight from Delphis, and the same re-entry problem has been known for like 15 years now – Michael Starberg

Instead, we next look at the .NET 2.0 BackgroundWorker class, which is pretty much better designed for such things. And here’s how its done.

Method 3: Using BackgroundWorker class (preferred)

Using the BackgroundWorker class is a multi-threading approach. Following the demo source code available at the top of this page, there are four main parts to this approach.

  1. WorkState is used to store the current working status of the task. It also stores the relevant data that will be used for updating the UI
  2. NubcakeWorker extends from BackgroundWorker, and this is the multi-threading processor. It contains three important event handlers namely DoWork, ProgressChanged, and RunWorkerCompleted. DoWork basically handles the event that starts the looping task, ProgressChanged handles the callback per iteration, and RunWorkerCompleted handles the event when the looping task ends or is terminated.
  3. MainForm is the UI that we want to update, and it contains two important methods. One is PerformTask that is invoked when a user clicks a start button. And the second is RefreshState that updates the UI, and it is called by the ProgressChanged method in NubcakeWorker per iteration.
  4. WorkTask is the task manager that performs the set of long running tasks. You can then update the UI by calling the worker.ReportProgress() function that invokes the ProgressChanged method to update the UI via RefreshState. That is ReportProgress -> ProgressChanged -> RefreshState -> UI updated.

As you can see, BackgroundWorker needs a bit more work to get right. But once you get the hang of it, its a pretty neat way to separate your tasks from your UI. And, AFAIK its the encouraged practice. Personally, I think all the methods work. It really depends on your project requirements and risks.

Anyway, generally multi-threading or multi-tasking can help improve the responsiveness of the program, and CPU prioritization can also benefit application performance. However, don’t forget that threading has some computational switching costs. So if there are TOO many threads, then each thread may not be given enough time to execute much during its time slice. So, do use these approaches in moderation.

  2 comments for “Multi-threading in C Sharp (C#)

  1. Hrucha
    June 30, 2012 at 2:01 am

    i Using Application.DoEvents (okay) how u take public class NubcakeEventHandler …..reply and in above all programs why u changed form names??

  2. July 10, 2012 at 5:00 am

    Hi Hrucha, none of the form names were actually changed. Download the tutorial files.

    If you notice, NubcakeEventHandler is a delegate (defined in NubcakeEventArgs.cs) that uses NubcakeEventArgs. This way, you would be able to define your custom event handling.

    Hope this helps.

Leave a Reply

Your email address will not be published. Required fields are marked *