WinForms and threading: The golden rules

DevToolsGuy / Monday, April 13, 2015

As a WinForms developer you constantly need to be aware of the way the application will work. Will there be some heavy calculation involved that can block the user interface or is it necessary to execute jobs simultaneously? The technique of threading can be very challenging, so in this post we will look at some golden rules for using threading correctly.

A thread is a piece of code in execution. Each application has at least one thread, this is called the main thread and is where the application starts. The main thread can create any number of threads, depending on the application requirements, to execute tasks in parallel. Compared to multiprocessing, in a multi threaded application the threads use the same process and therefore the same process addresses space. The threads do not need a special mechanism to communicate like multi process applications do (a multi process application uses IPC to communicate).

Why is threading important?

Let's explain the importance of threading with a simple example: consider you need to develop an application to calculate some complex mathematical formulas. While calculating the user wants to cancel the operation and starts another one. Doing this in an application with one single thread would simply make this impossible. The user would be unable to interact as long as the operation was running.

Now, good practice would be to execute such a time consuming calculation in another thread and use the main thread more efficiently to listen to the users’ input. When the main thread received a cancel signal it would stop the second thread. Using this approach would also result in a more responsive WinForms application because using one single thread would not only prevent the user from interacting but it would block the whole UI.

Basic steps in using threading

Using threading in a WinForms application looks like a complex task to carry out, but by implementing some basic steps, any application you build will benefit from it.

Invoke, BeginInvoke and EndInvoke

Never invoke any method or property on a control created on another thread other than Invoke, BeginInvoke, EndInvoke or CreateGraphics, and InvokeRequired. Each control is effectively bound to a thread which execute its code. If you try to access or change anything in the UI, like changing a background color from a different thread, your application will become unstable and can hang or react unexpectedly. The Invoke, BeginInvoke and EndInvoke methods have been provided so that you can ask the UI thread to call a method for you in a safe manner without blocking the main thread.

Prevent executing long running processes in the UI thread

When you execute a long running piece of code in the UI thread, all other code in that thread will be executed after that code is executed. This means that the controls in the application won't get repainted and any user interaction will lead to a hanging application.

You need to run that code in a separated thread like mentioned before. In .NET there is an Application.DoEvents() method that updates the main thread. When using this method the developer needs to decide when to call it. However, it is sometimes pretty tricky to debug in these scenarios so the advice is to not use this approach very often.

Learn to use delegates

A delegate can be defined as a data type which holds an array of methods. All the methods inside a delegate should have the same signature and the same return type. The method signature and the return type of a delegate are defined in the delegate declaration itself. So, the Thread class in the System.Threading namespace relies on the technique of delegate.

The Thread class constructor takes a ThreadStart delegate as a parameter (ThreadStart is a delegate defined in the System.Threading namespace.) and the signature of the ThreadStart delegate takes no arguments and returns void. So, the function to be executed in a separate thread should return void, and should not take any arguments. The following code shows an example:

public static void main()
{
            Thread t = new Thread(new ThreadStart(ClassName.MyFunction));
            t.Start();
            for(int i=0;i<100;i++)
            Console.WriteLine("I am in Main Thread {0}",i);
}
 
public static void MyFunction()
{
            for(int i=0;i<100;i++)
            Console.WriteLine("I am in Different Thread {0}",i);

}

Getting the basics

When developing WinForms applications a developer needs to know the basics of threading. Any application that runs in a single thread is a potentially unstable application because that single thread can block the UI and user interaction. By implementing some basic best practices like using the invoke methods or using delegates you will be able to build more robust applications. In the end, this will mean you can provide a better UX and get the satisfaction of having carried out some pretty complex developing.