C# – Synchronising with the GUI Thread

One of the golden rules when writing GUI applications with .NET (or any other framework for that matter), is that all GUI operations must be performed from a single thread, known as either the Application Thread or the GUI thread. If you are writing a multi-threaded application, then you must ensure that the other threads do not affect the GUI directly or indirectly.

So, how do you send a GUI update from another thread to the GUI thread?

I came across this problem in a case where I had no control over the other thread – that was running within a DLL provided by another company. My application installed a callback in the DLL which was called from another thread (see my previous post on using C# callbacks). I wanted that callback to update the user display.

The secret is to use a synchronisation context, which enables one thread to run a method on another thread. In this case, I want to run a method on the GUI thread.

In my application class I added the following declarations:

using System.Threading;
public partial class application
{
    private SynchronizationContext m_application_context = null;

    private void initialise_synchroniser()
    {
        m_application_context = SynchronizationContext.Current;
    }

   public void synchronise(SendOrPostCallback handler, object argument)
    {
        m_application_context.Post(handler, argument);
    }
}

The idea is that the initialise_synchroniser method is called from the GUI thread – I call it from within the Application’s Startup event handler. This ensures that the m_application_context field is set to the context of the GUI thread, i.e. the thread I want to synchronise with.

Then, the callback method called from another thread, calls the synchronise method, which posts a deferred call to the callback handler. The handler will later be run in the GUI thread.

The result is that update operations appear as pairs of methods – one to be called from the other thread and a matching method which will be called later, synchronised to the GUI thread. Any data required by the deferred function can be passed via the argument object, which can be any type derived from type object.

For example, in my application there is a message event generated by the DLL which passes a textual message as a string from the DLL to the application. Since the callback is called from another thread, this message needs to be synchronised before it can be displayed on the GUI.

The first stage is to write the callback which will be called from the other thread:

void message_callback(string data)
{
    synchronise(message_gui, data);
}

So, this calls the synchronise method with a handler called message_gui (so-called because it is synchronised to the GUI) and passes it a string (which is an object).

The synchronised method that is called in the GUI thread is then able to update the GUI:

void message_gui(object data)
{
    report_user_message(data as string);
}

This converts the object parameter back to the string type and then calls the method that updates the user message display in the GUI.

Leave a Reply