Using Async and Await to update the UI Thread Part 2

In a previous article on async and await, I showed a very simple example of how to run some code asynchronously. Then in the 2nd article I showed an example of updating the user interface in the main thread from an async method.

The code below (from the previous article) would execute a long running task which in this case counts to 5,000,000. As the task was running, on a set interval, the UI would be updated. This effectively decoupled the running of the task from the updating of the UI. You could have the task update the UI every 10ms, or you could update every 5 seconds. It really depends on what you are trying to do.

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsAsync
{
    public partial class Form1 : Form
    {
        private readonly SynchronizationContext synchronizationContext;
        private DateTime previousTime = DateTime.Now;

        public Form1()
        {
            InitializeComponent();
            synchronizationContext = SynchronizationContext.Current;
        }

        private async void ButtonClickHandlerAsync(object sender, EventArgs e)
        {
            button1.Enabled = false;
            var count = 0;

            await Task.Run(() =>
            {
                for (var i = 0; i <= 5000000; i++)
                {
                    UpdateUI(i);
                    count = i;
                }
            });

            label1.Text = @"Counter " + count;
            button1.Enabled = true;
        }

        public void UpdateUI(int value)
        {
            var timeNow = DateTime.Now;

            if ((DateTime.Now - previousTime).Milliseconds <= 50) return;

            synchronizationContext.Post(new SendOrPostCallback(o =>
            {
                label1.Text = @"Counter " + (int)o;
            }), value);             

            previousTime = timeNow;
        }
    }
}

A reader on Reddit suggested that you can write this code in a much more succinct way as shown below. The code is much simpler to see what’s going on, but the behaviour is slightly different. In this example, the code similarly counts upto 5,000,000 and updates the UI label. The await statement here will correctly restore the synchronisation context to update the UI thread which means you don’t have to deal with it manually (I didn’t realise that at the time). To enable the asynchrony of the task in this example we need to have a Task.Delay(1) as we are not using any other asynchronous objects in .NET for file or DB access for example.

private async void ButtonClickHandlerAsync(object sender, EventArgs e)
{
    button1.Enabled = false;

    for (var i = 0; i <= 5000000; i++)
    {
        label1.Text = "Counter " + i;
        await Task.Delay(1);
    }

    button1.Enabled = true;
}

The reason the behaviour is different in this case is that the Task.Delay actually delays the method which slows down the counter to 5,000,000 whereas in the original example, the updating of the UI was interval based which didn’t affect the running speed of the original method.

As an example I tweaked the example to count to 5000 instead and timed both versions with the StopWatch timer in .NET. The timing results were as follows :

  • My original code : 0.8 seconds.
  • Shorter version : 1 minute 22 seconds.

As you can see that’s quite a difference. So, to summarise, both versions do roughly the same thing, yet one has more code than the other. The original version decouples the timing of the UI updates from the purpose of the actual task, where-as the shorter and clearer version introduces a delay.

At the end of the day, you need to decide what matters more to you. As with anything in software development, there are many different ways of doing the same thing.

2 thoughts on “Using Async and Await to update the UI Thread Part 2

  1. Jiping

    [Thomas Levesque]’s solution seems logically right. And you do a little math, delay(1) for 5,000,000 times is roughly 1 minute 23 seconds.

    But, checked Microsoft official site, it says “do not rely on to keep a UI responsive.”

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s