Synchronizing calls to the UI in a multi-threaded application

synchronize

Introduction

More and more we are forced to write multi-threaded or asynchronous applications to e.g. leverage the many cores or cpu’s of our servers and personal computers.

But when dealing with multiple threads or asynchronous callbacks we often have to synchronize worker threads with the UI thread since visual components cannot be accessed from another thread that the one they have been instantiated in.

The synchronization of threads can be a tedious and error prone task if you don’t approach it correctly.

The problem

How many times have you seen code like the one below in a multi threaded WinForms application?

public void OnChangeStatus(StatusEventArgs e)
{
    if (this.InvokeRequired)
        this.BeginInvoke(new Action<StatusEventArgs>(OnChangeStatus), new[] {e});
    else
        lblStatus.Test = e.StatusMessage;
}

This code fragment checks whether it is running in the UI thread or not. If it is not running in the UI thread the call has to be synchronized with the UI thread by calling the BeginInvoke() method of the underlying control.

I confess that not too many years ago – maybe 3 or so – I was guilty of having implemented similar code as well. imageThen I got sick of all this repetitive code. Don’t repeat yourself (DRY) has always been one of my primary goals during my career as a developer. This was the time when I discovered that there is such a thing called SynchronizationContext in the .NET framework. I was then able to implement an event broker which handled all the synchronization for me and my UI code was freed from the burden of synchronization.

Lately a friend of mine came to me with the same kind of code I showed just above. He complained that such code sucks. I told him “You are right man! Been there…”

So let’s find a solution for this problem. I want to free up the UI code from having to synchronize callbacks to the UI thread. This should be done (auto-magically) by the infrastructure.

Approaching a solution

Working with events

event Imagine having a stock quote service that periodically returns you the current quote for a given symbol. The quote service runs in a background thread and notifies the UI thread via an event. Let me first show an implementation where the burden of thread synchronization has to be taken by the consumer of the quote service.

 

public class QuoteService
{
    private readonly string symbol;
    private decimal previousQuote;
    private bool stopRequested;
    private readonly Random random;
 
    public event EventHandler<QuoteEventArgs> QuoteChanged;
 
    public QuoteService(string symbol)
    {
        this.symbol = symbol;
        random = new Random((int) DateTime.Now.Ticks);
    }
 
    public void StartWorking()
    {
        var thread = new Thread(Run);
        thread.IsBackground = true;
        thread.Start();
    }
 
    public void Stop()
    {
        stopRequested = true;
    }
 
    private void Run()
    {
        while(!stopRequested)
        {
            decimal newQuote;
            if (TryGetNewQuote(symbol, out newQuote))
            {
                var args = new QuoteEventArgs(symbol, newQuote);
                OnQuoteChanged(args);
            }
            Thread.Sleep(500);
        }
    }
 
    private bool TryGetNewQuote(string symbol, out decimal quote)
    {
        // some stupid algorithm to simulate the stock exchange
        quote = previousQuote + (decimal) random.NextDouble() - 0.5m;
        previousQuote = quote;
        return true;
    }
 
    private void OnQuoteChanged(QuoteEventArgs e)
    {
        if(QuoteChanged!=null)
            QuoteChanged(this, e);
    }
}

The service is started by calling the StartWorking() method. This method spawns a new thread and starts it. The method that runs in the new (background-) thread is the Run() method. The Run() method runs infinitely until it is stopped by calling the Stop() method.

The Run() method gets active about every 500 ms. It tries to get a new quote from the stock exchange. The real stock exchange is just simulated with a very stupid algorithm here. Once a new quote is available a QuoteChanged event is raised such as the consumers can react. Unfortunately the event is raised on the same thread as the quote service is running, that is in the background thread.

So if the consumer has to update the UI we have a problem. The consumer needs to synchronize the callback from the quote service with the UI thread and only then can the UI be updated. Thus code similar to the one shown in the introduction is needed.

How can I force the quote service to trigger the QuoteChanged event on the UI thread? It’s easy when using the SynchronizationContext class of the .NET framework mentioned before. I introduce a new instance variable for the quote service

private readonly SynchronizationContext syncContext;

and initialize this field in the constructor

syncContext = AsyncOperationManager.SynchronizationContext;

Note that the AsyncOperationManager is another class of the .NET framework. It returns you the correct synchronization context for all application models supported by the .NET framework. In the case of a WinForms application the property SynchronizationContext returns an instance of type WindowsFormsSynchronizationContext. 

And finally I slightly modify my Run() method

private void Run()
{
    while(!stopRequested)
    {
        decimal newQuote;
        if (TryGetNewQuote(symbol, out newQuote))
        {
            var args = new QuoteEventArgs(symbol, newQuote);
            syncContext.Post(OnQuoteChanged, args);
        }
        Thread.Sleep(500);
    }
}

Instead of directly calling the OnQuoteChanged(…) method I use the synchronization context and post the call to OnQuoteChanged. The latter method is then executed on the UI thread. My consumer is now freed from the burden of having to synchronize any callback from the quote service.

Below I show a code fragment how a consumer could be implemented.

private QuoteService quoteServiceForIBM;
 
private void btnStart_Click(object sender, EventArgs e)
{
    quoteServiceForIBM = new QuoteService("IBM");
    quoteServiceForIBM.QuoteChanged += (s, qe) =>
                            {
                                lblQuote.Text = qe.Quote.ToString("n2");
                            };
    quoteServiceForIBM.StartWorking();
}

Here the consumer is a form (in a WinForms application). When the user clicks on a start button the quote service is initialized. The callback function to be executed when the QuoteChanged event is triggered is defined via a lambda expression. This time the new quote is shown in the label lblQuote. As you can see: there is absolutely no synchronization code needed in the consumer!

Working by registering lambda expressions

register

 

Now I want to present a asynchronous service where consumers can register lambda expressions as callback functions. Let me implement a temperature service. This service executes all registered lambda expressions whenever the temperature has changed.

 

 

 

public class TemperatureService
{
    private readonly SynchronizationContext syncContext;
    private bool stopRequested;
    private readonly List<Action<float>> actions;
    private readonly Random rnd;
 
    public TemperatureService()
    {
        syncContext = AsyncOperationManager.SynchronizationContext;
        actions = new List<Action<float>>();
        rnd = new Random((int) DateTime.Now.Ticks);
    }
 
    public void Start()
    {
        stopRequested = false;
        var thread = new Thread(Run);
        thread.IsBackground = true;
        thread.Start();
    }
 
    public void Stop()
    {
        stopRequested = true;
    }
    
    public void Register(Action<float> action)
    {
        actions.Add(action);
    }
 
    private void Run()
    {
        while (!stopRequested)
        {
            var newTemperature = (float) (rnd.NextDouble()*20.0-10.0);
            syncContext.Post(e => OnTemperatureChanged((float)e), newTemperature);
            Thread.Sleep(1000);
        }
    }
 
    private void OnTemperatureChanged(float temperature)
    {
        foreach (var action in actions)
            action(temperature);
    }
}

The code is very similar to the one of the quote service presented in the previous section. But here the service exposes no events. Instead it has a Register(…) method where consumers can register themselves with a a lambda expression (or callback method). The lambda expression must have one argument of type double which corresponds to the temperature.

The service notifies all registered consumers every 1000 ms by calling their respective lambda expression and providing the current temperature (in the method OnTemperatureChanged(…)).

The consumer code looks very similar to the one presented in the previous section

private TemperatureService temperatureService;
 
private void btnStartTempService_Click(object sender, EventArgs e)
{
    temperatureService = new TemperatureService();
    temperatureService.Register(t => lblTemperature.Text = t.ToString("n2"));
    temperatureService.Start();
}

The only difference being the call to the Register(…) method instead of “consuming an event”.

Well that’s it, or is it? No! That cannot be the final solution… Our services are now in charge of the synchronization. This is only a shift of the burden not a real solution! We have to find a more generic solution!

Working with a message broker or message bus

bulb Let’s lean back for a moment and reflect on what really happens. What we are really doing is to exchange messages between (service-) providers and consumers. So what if we could just introduce a man in the middle which does the dirty task of synchronization for us? Any provider just posts its messages to this man in the middle (let us call it message broker for now). And any consumer registers with the broker for specific types of messages. The broker then just receives messages and distributes them to all registered consumers. Before distributing a message it synchronizes the threads.

The code below shows a (very simplistic) implementation of such a message broker. A provider can use the Send<T>(…) method to post a message and a consumer can register itself by using the Register<T>(…) method. Here T represents the message type.

public class MessageBroker
{
    private readonly Dictionary<Type, object> consumers;
    private readonly SynchronizationContext synchronizationContext;
 
    public MessageBroker()
    {
        consumers = new Dictionary<Type, object>();
        synchronizationContext = AsyncOperationManager.SynchronizationContext;
    }
 
    public void Send<T>(T message)
    {
        var key = typeof(T);
        if (!consumers.ContainsKey(key)) return;
 
        var list = consumers[key] as List<Action<T>>;
        list.ForEach(action =>
            synchronizationContext.Post(m => action((T)m), message));
    }
 
    public void Register<T>(Action<T> action)
    {
        var key = typeof (T);
        List<Action<T>> list;
        if (!consumers.ContainsKey(key))
        {
            list = new List<Action<T>>();
            consumers.Add(key, list);
        }
        else
            list = consumers[key] as List<Action<T>>;
        list.Add(action);
    }
}

Please note that the synchronization with the UI thread happens in the last line of the Send<> method.

To access the message broker from allover my application I use the singleton pattern. I just define a static wrapper MessageBus around my broker.

public static class MessageBus
{
    private static MessageBroker messageBroker;
 
    private static MessageBroker Broker
    {
        get
        {
            if (messageBroker == null)
                messageBroker = new MessageBroker();
            return messageBroker;
        }
    }
 
    public static void Send<T>(T message)
    {
        Broker.Send(message);
    }
 
    public static void Register<T>(Action<T> action)
    {
        Broker.Register(action);
    }
}

Note that in a real application I would “never” use this approach but rather use an IoC container which provides me access to the message broker via dependency injection (DI). But for this simple sample I didn’t want to introduce DI and IoC.

I can now modify my quote service such as that it uses the message broker. Then the Run() method looks like this

private void Run()
{
    while (!stopRequested)
    {
        decimal newQuote;
        if (TryGetNewQuote(symbol, out newQuote))
        {
            var message = new QuoteMessage(symbol, newQuote);
            MessageBus.Send(message);
        }
        Thread.Sleep(500);
    }
}

Instead of QuoteEventArgs I use a message of type QuoteMessage to transport data. The implementation of this message type is trivial

public class QuoteMessage
{
    private readonly string symbol;
    private readonly decimal quote;
 
    public QuoteMessage(string symbol, decimal quote)
    {
        this.symbol = symbol;
        this.quote = quote;
    }
 
    public decimal Quote { get { return quote; } }
    public string Symbol { get { return symbol; } }
}

The consumer code is shown below

private QuoteService quoteServiceForABB;
 
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    MessageBus.Register<QuoteMessage>(m => lblQuote.Text = m.Quote.ToString("n2"));
}
 
private void btnStartQuoteSvc_Click(object sender, EventArgs e)
{
    quoteServiceForABB = new QuoteService("ABB");
    quoteServiceForABB.StartWorking();
}

In the OnLoad(…) method of the form I register the form as a consumer of messages of type QuoteMessage and pass as a parameter the lambda expression that shall be executed whenever such a message is available.

Summary

Working with multi-threaded or asynchronous applications is a little bit harder than with single-threaded applications. One of the problems is that code running in background threads cannot directly access visual components of the UI. One has to synchronize the background thread with the UI thread. This post presented various approaches how one can solve this problem. The goal of this post is to introduce a possible solution which generalizes the thread synchronization and concentrates it into a central place (into the infrastructure of the application) such as that the code in the presentation logic and/or in the services does not have to care about synchronization. The solution presented here is a message broker or message bus. Service providers can post messages to the broker and consumers can register themselves for specific messages. The thread synchronization is handled by the broker.

Related Articles:

    Post Footer automatically generated by Add Post Footer Plugin for wordpress.

    About Gabriel Schenker

    Gabriel N. Schenker started his career as a physicist. Following his passion and interest in stars and the universe he chose to write his Ph.D. thesis in astrophysics. Soon after this he dedicated all his time to his second passion, writing and architecting software. Gabriel has since been working for over 12 years as an independent consultant, trainer, and mentor mainly on the .NET platform. He is currently working as chief software architect in a mid-size US company based in Austin TX providing software and services to the pharmaceutical industry as well as to many well-known hospitals and universities throughout the US and in many other countries around the world. Gabriel is passionate about software development and tries to make the life of developers easier by providing guidelines and frameworks to reduce friction in the software development process. Gabriel is married and father of four children and during his spare time likes hiking in the mountains, cooking and reading.
    This entry was posted in asynchronous, multi threading. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
    • http://www.josenet.com jose

      man this is really cool specially the message broker, but i would not have implemented it like that because the consuer who uses the quote service is not aware that the message will come back using the message bus. Also what would happen to my consumer is created and it register for events to the message bus, but then my consumer gets disposed? the event still fires even though my consuer is gone? would it be null reference or object not instantiated or something?

    • http://www.lostechies.com/members/gnschenker/default.aspx Gabriel N. Schenker

      @jose: I agree that the un-register is missing in the message broker sample. But I just wanted to show the concept. For usage in a real app the code would have to be extended…

    • Ken

      Excellent article. I’ve been looking at incorporating a message broker which took care of synchronizing calls to the UI. Couldn’t believe my luck when I googled and found this article.

      Quick question regarding UnRegistering. What do I pass to an UnRegiste method? I supposethe message type, which is the dictionary key. But how do i then loop through the list and remove the correct Action for a specifc message type?

      Thanks.

    • http://reachingforlucidity.net/live/stadtplan-berlin.php

      Gut!

    • http://reachingforlucidity.net/live/stellenangebote-berlin.php

      Gut!

    • http://jefflouella.com/mp3/fussball/

      Gute Arbeit hier! Gute Inhalte.

    • http://jefflouella.com/mp3/fussball/

      Gute Arbeit hier! Gute Inhalte.

    • http://seeworthy.org/ducktales/gesundheit/

      Sehr wertvolle Informationen! Empfehlen!

    • http://azadnevis.wordpress.com Mehdi

      I learned a lot from this clear and easy DRY tutorial. This is exactly what i was looking for, Thank you very much.

    • Eric

      Small beginner question: What is the QuoteEventArgs. I get this error: “type or namespace could not be found.” in this line public event EventHandler QuoteChanged;

      If it’s a custom class could you provide an example one?

      Thanks for the article it is very nice.

    • http://www.lostechies.com/members/gnschenker/default.aspx Gabriel N. Schenker

      @Eric: this is just a class inheriting from EventArgs and having two properties Symbol (string) and Quote (decimal) and a constructor which allows to set these properties.

    • annamalai

      i am getting error in this line 
      syncContext.Post(OnQuoteChanged, args); as follows
      The best overloaded method match for ‘System.Threading.SynchronizationContext.Post(System.Threading.SendOrPostCallback, object)’ has some invalid arguments

      Argument 1: cannot convert from ‘method group’ to ‘System.Threading.SendOrPostCallback’

      the function signature is same as mentioned in the example. 
      please help is there any changes i need to make

      • annamalai

        i fixed it by 
        changing syncContext.Post(OnQuoteChanged, args);
        to 
         syncContext.Post(delegate
                    {
                        OnQuoteChanged(arg);
                    }, null);

    • Freon

      Fantastic little mechanism that solves the (thread)=>GUI problem nicely.  Is scaleable and simple.  WIN!

    • Mike

      Million-dollar article!  Thanks a million!  ;)

    • Groverboy

      Very nice generalised design, thanks for sharing this.  I’ve been using SynchronizationContext for some time and definitely find it preferable to using Control.BeginInvoke.

      A minor criticism: cancellation does not look thread-safe.  The worker thread reads class field stopRequested (QuoteService.Run) and the consumer thread writes to the field (QuoteService.Stop), but there is no thread synchronisation to prevent a race condition.  This means the consumer may call Stop and begin writing to stopRequested when the worker thread reads the field (stopRequested still equals false), resulting in an additional iteration beyond the consumer request to cancel.

      Thread synchronisation can be implemented by using either a lock statement or qualifying stopRequested by ‘volatile’.

      So far as I know the race condition here has a minor consequence (potentially delayed cancel) unlike, for example, deposit to/withdrawal from a bank account.  Can anyone think of a situation in which delayed cancel becomes a significant problem?