Chapter 13

Multithreading and the BackgroundWorker class

Multithreading is the ability of an operating system to run several threads in "parallel" (even if there is only one CPU with a single core). A thread is just part of a process that can run independently from other threads. Windows can multithreading since Windows 95 (and Windows NT 3.1 of course).

The .Net runtime offers multithreading through a set of classes in the System.Threading namespace. A modern alternative is the namespace SystemThreading.Task with its Task class for asynchronous calls. Asynchronous execution will be discussed in another chapter.

Creating single threads is seldom used in Windows applications. if there is a need for parallelizing certain tasks, the BackgroundWorker class and the Task class are much better options for application developers.

Starting new threads

Starting a new thread is simple both in a console or in a UI app. Since there is always one "main thread" running from the beginning, the new thread is called a background thread. But technically there is no distinction between different threads. The one notable difference is that when a foreground thread terminates, the process terminates too.

using System.Threading

var tStart := ThreadStart{DoSomething}
var t := Thread{tStart}
t:Start()

DoSomething() is just a regular method that takes no arguments (and prints out the ID of the current thread in the example).

There is no need to create a Delegate first as the argument for the ThreadStart constructor. Besides showing how simple starting a new thread can be, the example does not have any practical value.

Example 13.1: (XS_SimpleThread.prg) ** You will find the source code in the repository

The first thing to keep in mind is that a thread is not a scheduled task. No scheduler takes care of the "housekeeping". A thread has to be started and it automatically stops when there is nothing more to do. The challenging part is not creating and starting threads, but synchronizing several running threads, avoiding so-called "race conditions" where it's not possible to foresee which thread ends first, and updating the UI.

Another difficult question to answer is if creating new threads has a benefit and is not only adding complexity. The only thing to consider inside a WinForms or WPF app is that the .Net Runtime does not allow any background thread updating the "UI" (the forms and their controls) which is always created on the main thread. There is an extra step involved by calling the BeginInvoke() method of control. More on this later.

One of the few cases where multithreading in a console application is an absolute necessity is when the console application has to handle multiple requests like with an HTTP request handler.

Locking regions of code

As soon as a program uses multiple threads, any piece of code that is currently executing can be interrupted at any time by another thread. As soon as the new thread changes a variable that is visible outside the thread code, it might be necessary to lock the code region so that the execution of the commands in this region cannot be interrupted by another thread. Whereas C# offers a lock, the X# syntax BEGIN LOCK/END LOCK is better readable and consistent with the syntax of other control commands.

The following example is a typical textbook example you will find in many tutorials. I didn't bother to change it very much because each case where locking might be necessary is unique. On the other hand, applying a lock is easy. So the main purpose of the example is to show why locking is necessary and that applying a lock in X# is simple.

The purpose of the sample is to output a sentence in two steps. Because the function that does the output is run several times on several threads, it can happen that after the first part of the sentence has been printed the function is interrupted by another thread that doesn't anything about the other threads what they printed so far. So this thread is also printing the first part of the sentence. Then another thread might take over and print either the first or the second part of the sentence. So the whole output will be every time a (different) mixture of the first and the second part of the sentence.

The only way to avoid this mess is to lock the sequence of commands that do the output with Begin Lock and End Lock.

**Example 13.2: (XS_ThreadLock1.prg) **

// A simple thread Lock example
using System.Threading

// Create a ref object - type and name doesn't matter
Static objLock := Object{} As Object

Function ShowMessage() As Void
    Begin Lock objLock
      Console.Write("VO and .Net finally together ")
      Console.WriteLine("thanks to X#")
    End Lock

Function Start() As Void
  var t1 := Thread{ShowMessage}
  var t2 := Thread{ShowMessage}
  var t3 := Thread{ShowMessage}
  t1:Start()
  t2:Start()
  t3:Start()


NOTE: Its worth noting that the variable objLock in the example is a static GLOBAL.


More on multithreading

There is so much more to say about multithreading like thread states, synchronization through mutexes, semaphores or monitors and the important Local Thread Storage (LTS) that it would require another book (of course, several books have been written about this subject - one that helped me a lot was the excellent book Threading in C# from Yoseph Albahri who is also the author of the LINQPad tool - you can find more about his book https://www.albahari.com/threading.

Multithreading in a WinForms or WPF application

Multithreading in a WinForms or WPF application is the same as in a console application when it comes to which classes to use etc. But there this a big difference. It arises from the fact that since the beginning of time (that is 1985 when Windows 1.01 was released to the market as a nice UI on top of MS-DOS for playing Reversi and running the first version of Excel) the Windows UI run on the main thread. That means that any "UI package" that sits on top of Windows is bound to this constraint.

Any GUI application can use as many threads as it likes, but accessing the main thread to update the UI can (or should) only be accomplished "safely". In WinForms, any control (and that means the Form class too since it inherits from the Control class) offers the BeginInvoke() method that expects a delegate that points to the program code that should safely access a UI element like a TextBox or a ListBox on the main thread.

Example 13.3: (XS_BeginInvoke_Example) ** You will find the source code in the repository

Accessing the UI from a background thread usually results in an InvalidOperation exception. But it can be done with a little extra effort. The Control class offers a BeginInvoke() method that accepts a delegate as its first argument. Although the main purpose of this method is to run the method the delegate points to asynchronously, a side effect is that accessing the UI thread is "safe" and does not result in an exception.

Alt Accessing a control from a background thread results to an InvalidOperationException Fig 13.1: Accessing a control from a background thread results in an InvalidOperationException

// necessary to do BeginInvoke?
if Self:lblLabel1:InvokeRequired
    //Use the very helpful MethodInvoker class
    Self:lblLabel1:BeginInvoke(MethodInvoker{Delegate{UpdateLabel(i"{i}")}})
end if

The simple task of assigning some text to the Text property of the label becomes a little complicated. First, an instance of the MethodInvoker class (namespace System.Windows.Forms) is created. It takes an instance of a Delegate as its only argument. The X# delegate either accepts a method name or an anonymous function. Although the method does nothing other than setting the Text property of the label, it could access other controls as well. And it's possible to pass more arguments with BeginInvoke() of course.

Method UpdateLabel(msg as String) As Void
    Self:lblLabel1:Text := msg  

The DoEvents() method for better UI updating

In some cases, there is no need for a background thread or the BackgroundWorker class. If updating the UI does not happen and/or the form freezes temporarily, there is a simpler solution by calling the DoEvents() method of the application class (namespace System.Windows.Forms). It enables the application to process messages that are waiting in the message queue for the main window (a message is just a number in this case). Although DoEvents() can be called anytime, it's most appropriate at the end of a loop inside a long-running operation or after a long-running operation is completed.

DoEvents() enables the form window to process its messages. One of these messages makes the label control update its content.

Example 13.4: (XS_DoEvents_Example) ** You will find the source code in the repository

A small WinForms sample application shows what a difference a DoEvents() call can have. If the checkbox is not set, the listbox won't updated regularly and the window can't be moved smoothly. Just set the checkbox and notice the difference. Although DoEvents() does no magic. If the SelectedIndex property is set to the index of the last added element, for example, the list box will update its content without DoEvents().

Alt Calling DoEvents() enables the list box can be updated and the form window can be moved Fig 13.2: Calling DoEvents() enables the list box can be updated and the form window can be moved

Using DoEvents() is a simple technique to keep a WinForms app responsive without having to use background threads.

Multithreading means running one or more background threads

Multithreading in a typical "Desktop application" means that any longer-running task should run a background thread. There is nothing special about a background thread in contrast to a "regular" foreground thread. It's more about doing something invisible "in the background".

There are at least two options for creating background threads:

  1. By creating the thread directly.
  2. By using the BackgroundWorker class that has been part of the .Net Framework since version 2.0.

Creating a thread directly is usually not an option, so let's have a look at the very convenient BackgroundWorker class.

The BackgroundWorker class for simple background threads

With the BackgroundWorker class using background threads becomes easy. The class is part of the .Net runtime since version 2.0.

The many benefits of a BackgroundWorker are:

  • A long-running task is executed on a separate thread and does not block the main thread.
  • No "UI freezing"
  • Accessing the UI is simple both in the ProgressChanged and the RunWorkerCompleted event handler.
  • Reporting a progress becomes easy.
  • Cancellation of the job becomes easy.
  • An event is raised when the background worker has completed their work.

A sample project for using the BackgroundWorkerClass

The BackgroundWorker class is not difficult to use, but it offers, like the FileSystemWatcher class, several options that can be confusing at first use. As usual, an example is much better than a copy and paste of the official documentation.

Example 13.5: (BackgroundWorker_Example) ** You will find the source code in the repository

So, I created a small WinForms application that uses all the options of the BackgroundWorker class. It scans a given directory of png files and fills a list with the name of each file. There is a small preview as well. The idea is to show a task that would usually freeze the window but since the BackgroundWorker class takes care of the background thread this does not happen. Another advantage is that the programming is clean and simple.


TIP: In Visual Studio, the BackgroundWorker can be dragged to the form designer as a component. All its properties and events are available through the properties window. Besides that, there are no benefits to using a component.


Alt A small WinForms apps demonstrates the benefits of the BackgroundWorker class Fig 13.3: A small WinForms app demonstrates the benefits of the BackgroundWorker class

Initializing the BackgroundWorker

Initializing a BackgroundWorker is simple.

Self:Worker := BackgroundWorker{}
Self:Worker:WorkerReportsProgress := True
Self:Worker:WorkerSupportsCancellation := True
Self:Worker:DoWork += DoWorkEventHandler{WorkerDoWork}
Self:Worker:RunWorkerCompleted += RunWorkerCompletedEventHandler{WorkerCompleted}
Self:Worker:ProgressChanged += ProgressChangedEventHandler{WorkerProgressChanged}

The most important setting is connecting the DoWork event handler.

Starting the BackgroundWorker

A call to the RunWorkerAsync() method starts the BackgroundWorker. It might seem a little strange at first that the method does not accept any arguments. The idea is, that all the commands that the BackgroundWorker should "work" are part of the DoWork event handler. This is where the action takes place.

Self:Worker:RunWorkerAsync()

There is nothing special about the DoWork event handler. The only thing to remember is, that accessing the UI on the main thread still would require calling BeginInvoke(). The event handler for accessing the UI is the ProgressChanged event handler.

Reporting progress

If the WorkerReportsProgress property had been set to true, calling the ReportProgress() method inside the DoWork event handler raises a ProgressChanged event. In this event handler, like in the RunWorkerCompleted event handler, accessing the UI can be done without having to resort to the BeginInvoke() method.

Calling ReportProgress() is done with a percentage value:

Self:Worker:ReportProgress(i)

The current percent value is accessible through the ProgressPercentage property of the ProgressChangedEventArgs argument.

Private Method WorkerProgressChanged(Sender As Object, e As ProgressChangedEventArgs) As Void
    var fileIndex := e:ProgressPercentage

Cancelling the worker

If the WorkerSupportsCancellation property had been set to true, calling the CancelAsync() method inside the DoWork sets the CancellationPending property to true. This time, there is no event raised. The property has to be "queried" during the DoWork event handler whenever it's appropriate.

// Cancel pending?
if Self:Worker:CancellationPending
   Return
EndIf 

Getting notified when the worker is done

When the worker is finished with his work, a RunWorkerCompleted event is raised. It could be used for resetting the Progressbar if this is not happening by itself. Like with the ProgressChanged event handler, the UI on the main thread can be accessed directly.

Summarizing the benefits of the BackgroundWorker

Using the BackgroundWorker usually is a "no-brainer" since not using it may have several disadvantages. You can try it out by unchecking the checkbox in the WinForms sample application. You will notice that although the list box and the progress bar are updated, you cannot move the form, it's not possible to cancel the operation because the button is not clickable most of the time (the event handler wouldn't work anyway) and there is no preview for many of bitmaps because the internal update process is blocked by the long running process (and the "redraw yourself" messages are stuck in some internal buffer).