In a previous article on async and await, I showed a very simple example of how to run some code asynchronously. In this article, I want to take this a little further with another example.

I have recently had to go and maintain an old c# Winforms application (yes I know winforms is a little old and considered the new vb6 but the application works and still provides value) that needed some new features adding. The problem with this application is that is feels a little sluggish for the user and constantly locks up, or gives the impression that it has crashed which means users have been trying to kill the application when really, it is processing a lot of work and the UI has become unresponsive.

Whilst I was in the code base, I wanted to try and improve some of this unresponsiveness by moving code out into tasks and asynchronously updating the UI. In this article I will cover how to do this.

Example of Unresponsive Application

First lets start with a small example. Below is a screen shot of a very simple winforms app. It doesn’t really do much. When you load it up, you press the button and the application counts to 5,000,000. I want this count to be displayed on the screen.

Simple Application for an Async and Await Example
Simple Application for an Async and Await Example
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void ButtonClickHandler(object sender, EventArgs e)
    {
        for (var i = 0; i <= 5000000; i++)
        {
            label1.Text = @"Counter = " + i;
        }
    }
}

In the code above, when you click the button in the application it calls the ButtonClickHandler which starts a count to 5,000,000. Inside the for loop we also try to update the text on the label to tell the user how far the count has got.

When you load up the application and click the button, nothing happens, and about a minute later, the label updates to say “Counter = 5000000”. Whilst the application is counting, the user interface becomes completely unresponsive. You can’t drag the window, or resize it. This is the issue I have been facing in this application that I need to maintain. Fortunately, fixing this issue is quite straight forward with async await.

Example of Responsive Application

First of all, lets look at the completed code behind file.

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;
        }
    }
}

Let’s take a look at the code. First of all we have a private SynchronizationContext object. The SynchronizationContext class is a class belonging to the .NET Framework’s System.Threading namespace. The purpose of this class is to provide a model to make communication between threads easier and more robust. Then we also have a private DateTime which is used for controlling the time interval of an update back to the UI thread. The SynchronizationContect.Current is set to the current context of the user interface in Form1’s constructor.

    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;
    }

Next we have a button event handler. This is attached to the button on the main form and kicks off the task to count to 5,000,000. As part of this method we also call a method called UpdateUI() and this is the method that will call back to our main UI thread.

    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;
    }

First the UpdateUI method grabs the current time and subtracts it from the previousTime which was defined at the beginning of the class. Then a check take place to see if the timeNow time is within 50ms of the previousTime. If it is within 50ms, then the method returns, otherwise it carries on. Using this check we can change how frequently we want to update the UI. Sometimes it is not necessary to update the user interface that quickly. Once a second may be fine. It depends on what you are doing.

Next we post a call to the SynchronizationContext to update the UI. This is done by synchronizationContext.Post which calls a delegate which executes the code in-between the braces on the UI thread.

Difference between SynchronizationContext.Send and SynchronizationContext.Post

In the snippet above you call into the UI thread using SynchronizationContext.Post, but you can also do this with SynchronizationContext.Send, but what is the difference? Send is a synchronous operation and will wait for an answer or action completed from the call, where as Post is asynchronous where it makes the call to the UI thread and continues without waiting for a reply. In this example it wouldn’t appear to make much difference which of the 2 you use, but we have used Post as we do not require to wait for a reply as we are just posting some data to be shown on the screen.

Wrapping Up

This is another simple example that will help you to make your applications more responsive. Although this example used Winforms as that is the space I am in at the moment, it is just as relevant for WPF too.

Advertisements

One comment

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