Async, Await, Tasks, and UI synchronization with C# 5, .NET 4.5 and WinForms

Microsoft .NET

I'm going to cover a easy example on the new asynchronous programming model in C# 5 and .NET 4.5 with the recent release of Visual Studio 11. The example we will be writing is a small WinForm that will calculate the square root of a large number while providing progress updates to the UI, and moreso, doing all of this asynchronously, and safely.

The first note is that all this can be done right inside our form class (eg. Form1.cs), so I won't include a ZIP file or source download, the example should be very straight forward and simple. Let's dig right in.

To start with, we're going to create a SynchronizationContext. This part is not anything new, and has been around since .NET 2.0 so I won't be covering it. In short, it's just a very helpful object that allows you to perform synchronization between threads or other asynchronous environments. Add a field, and initialize it in the constructor.

public partial class Form1 : Form 
{
    public Form1() 
    {
        InitializeComponent();
        m_SynchronizationContext = SynchronizationContext.Current;
    }

    private SynchronizationContext m_SynchronizationContext;
}

We will be using this to invoke calls to the UI thread safely later on. Next, let's declare our async method.

private async void ComputeSquareRootAsync(object sender, EventArgs e) 
{
    double sqrt = await Task<double>.Run(() => 
    {
        double result = 0;

        for (int i = 0; i < 5000000; i++)
        {
            result += Math.Sqrt(i);
        }

        return result;
   });
}

There are a few things to note here. One, notice the declaration private async void. Here we are telling the compiler that this method will be intrinsically asynchronous, and because the method will contain awaiters, the C# compiler will know to rewrite our method appropriately under the hood.

If you don't know already, async and await simply work off of the existing Task objects in the framework. By telling the program to await Task.Run, we are saying "Run all code up until this point synchronously, then run the task on a background method, but don't block the UI, and return control to the caller when the result is returned". This means that while our Task is running and doing some work off on a background thread, our program will not continue the normal flow of execution until the result is returned, but at the same time will not block the calling context.

Now drag a button onto your form (eg. button1) and set it's click event to our ComputeSquareRootAsync method. Now drag a label (eg. label1) and a progressbar (eg. progressBar1). Let's update our method a bit.

private async void ComputeSquareRootAsync(object sender, EventArgs e) 
{
    label1.Text = "Calculating sqrt of 5000000";
    button1.Enabled = false;
    progressBar1.Visible = true;

    double sqrt = await Task<double>.Run(() => 
    {
        double result = 0;

        for (int i = 0; i < 5000000; i++)
        {
            result += Math.Sqrt(i);
        }

        return result;
   });

   label1.Text = "The sqrt of 5000000 is " + sqrt;
   button1.Enabled = true;
   progressBar1.Visible = false;
}

Set the label's initial text in the designer to "Click the button to begin", and the progress bar's visiblility to false initially. I also set the button's text to "Calculate". This is just cosmetic, but we are making a small, but not really practical, good demo app.

Now what will happen is that the square root will be executed asynchronously while not blocking the UI, but at the same time the last three lines of code that update the controls will not execute until the result is returned (eg. the Task is returned). This is the magic of the new async model.

Let's implement our progress updates now. To do this, we're going to implement a new interface onto our form that is provided by .NET 4.5 explicitly for this scenario, and it is called IProgress(Of T).

To make things simple, we will use IProgress(Of Tuple(Of int, int)) (eg. IProgress>) so we can pass in the maximum value and current value of our progress operation (eg. computing the square root).

public partial class Form1 : Form, IProgress<Tuple<int,int>>

Now implement the interface's Report method.

public void Report(Tuple<int, int> value) 
{
    DateTime now = DateTime.Now;

    if ((now - m_PreviousTime).Milliseconds > 20) 
    {
        m_SynchronizationContext.Post((@object) => 
        {
            Tuple<int, int> minMax = (Tuple<int, int>)@object;
            progressBar1.Maximum = minMax.Item1;
            progressBar1.Value = minMax.Item2;
        }, value);

        m_PreviousTime = now;
    }
}

Now you will notice one thing off the bat, I included a reference to a DateTime value named m_PreviousTime. Add this as a field in the Form1 class and set its value to DateTime.Now.

private DateTime m_PreviousTime = DateTime.Now;

You could also do that in the constructor where we initialized our synchronization context. The method is simple. We are creating an anonymous function that passes in an object instance of our Tuple as a parameter, which we explicitly convert through an explicit cast. Through the usage of the Post method, it is actually calling this anonymous function on the synchronization context, which is actually our UI thread, thus giving us a safe UI update and no cross-thread violations. This is similar do doing a Control.Invoke, and I suspect somewhere under the hood it may actually do that but I haven't looked at the implementation yet.

And now we're done. Here's the full Form1 class code so you can make sure you implemented all the steps.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Async {
    public partial class Form1 : Form, IProgress<Tuple<int,int>> {
        public Form1() {
            InitializeComponent();
            m_SynchronizationContext = SynchronizationContext.Current;
        }

        private SynchronizationContext m_SynchronizationContext;
        private DateTime m_PreviousTime = DateTime.Now;

        private async void ComputeSquareRootAsync(object sender, EventArgs e) {
            label1.Text = "Calculating Sqrt of 5000000";
            button1.Enabled = false;
            progressBar1.Visible = true;

            double sqrt = await Task<double>.Run(() => {
                double result = 0;

                for (int i = 0; i < 5000000; i++) {
                    result += Math.Sqrt(i);
                    Report(new Tuple<int,int>(5000000, i));
                }

                return result;
            });

            progressBar1.Visible = false;
            button1.Enabled = true;
            label1.Text = "The sqrt of 5000000 is " + sqrt;
        }

        public void Report(Tuple<int, int> value) {
            DateTime now = DateTime.Now;

            if ((now - m_PreviousTime).Milliseconds > 20) {
                m_SynchronizationContext.Post((@object) => {
                    Tuple<int, int> minMax = (Tuple<int, int>)@object;
                    progressBar1.Maximum = minMax.Item1;
                    progressBar1.Value = minMax.Item2;
                }, value);

                m_PreviousTime = now;
            }
        }
    }
}

I may come back and add the source when I have more time.

8 Comments

  1. Silas Pereira

    Fantastic example. I wish all pseudo articles about new technologies could be so straight to the point as yours.

    Nice example, shows exactly what it is supposed to show, and very clear explanation.

    Thank you.

  2. Stefan

    Hi, thanks. I’m a beginner. Please send the code as a zip, i tried to code it, but i fail. It dosn`t work on my laptop. But i think, it was my falt.

    Stefan

  3. Tory Netherton

    Excellent Post. You really made the subject clear. Everything I had read prior to this was confusing and caught up in the way things used to work. A concise example of how to use the current system is exactly what I needed. Thank you.

  4. Aswathy

    This post is very helpful.Thank you.

  5. Marcelo

    Excelent ! Thank you very much.

  6. Marcus Fehde

    Thank you for this post. I was looking for a real basic introduction to async/await, but most introductions start with an overloaded example.
    So, this one was perfect.

  7. Will

    Thanks for this. I’ve come late to trying to learn asynchronous programming… Just in time for .Net 4.5. So many articles and blogs talking about how async/awat simplify keeping the UI responsive, but not many actually explain IProgress and how to get your background threads talking to the UI.

    1. David Anderson (Post author)

      Thanks for the feedback; I’m glad that my article helped you. I get irritated as well when I need to learn something, and articles I read leave out some of the most important parts. I try not to do that in my articles.

Leave a Comment