UI Responsiveness With Async in C# 5.0

It's time to revise one of my earlier posts based on a new feature of C# 5.0.  In UI Responsiveness, I had shown how to use threading and delegates to keep the UI responsive while the program was busy running a CPU-bound method in the background.  C# 5.0 has introduced the new async and await keywords that will allow us to accomplish the same goal in a much simpler way.

Note: This example now requires Visual Studio 2012.

Async

Async is used as a method modifier that marks the method as being able to run asynchronously.  Adding this modifier to the method declaration is a required step and indicates to the compiler that the method is async-ready.  Next, we'll append Async to the end of the method's name to indicate to ourselves, and anyone using our code, that this is an async method.  This change of the method's name is not required but is considered a best practice.  In general, you would also have to change the return type to either Task, or Task<T>.  However, in our example, we will be using async with an event handler, and in these cases the return type must remain void.
private async void StartButtonClick(object sender, RoutedEventArgs e)
{
    // event handler code
}

Await

Await is used to indicate two things: where the calling method should wait, and what the program should be doing while the calling method is waiting.  The program will run synchronously until it gets to the await command within the async method.  At this point, it will run asynchronously.  It starts the method indicated by the await command and also returns control to the async method's caller (in our case, this caller would be the UI).  The async method will wait here until the await command finishes.  The calling function will run until it encounters an await command itself.  Once the current async method completes the await command and the calling function has hit its next await command, the async method becomes synnchronous again.
double result = await Task.Run(() => this.ProcessStuff(maxCount));

Success

Using this new feature, the length and complexity of our sample from the UI Responsiveness post has been greatly reduced.  We no longer need any delegates nor do we need to explicitly handle creating or starting any threads.

Final Code

This code below will work with the XAML file defined in the UI Responsiveness post with one change: the method name for the startButton's Click handler has been changed from StartButtonClick to StartButtonClickAsync.
namespace WpfApplication1
{
    using System;
    using System.Threading.Tasks;
    using System.Windows;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        // This method is marked with the new async modifier and its
        // name ends with Async.
        private async void StartButtonClick(object sender,
                                            RoutedEventArgs e)
        {
            // Indicate that the program is busy with the user's input.
            this.startButton.IsEnabled = false;
            this.startButton.Content = "Working";

            // Get user input to use for a work load in our Process
            // function. Note there is no input validation here.
            int maxCount = Convert.ToInt32(this.userInput.Text);

            // Create a new Task that will run on a background thread
            // and await the results.
            double result = await Task.Run(() =>
                                          this.ProcessStuff(maxCount));

            // Display the results.
            this.resultDisplay.Text = result.ToString();

            // Indicate that the program is ready for a new input.
            this.startButton.Content = "Start";
            this.startButton.IsEnabled = true;
        }

        private double ProcessStuff(int maxCount)
        {
            double result = 0;

            for (int i = 0; i < maxCount; i++)
            {
                for (int j = 0; j < maxCount; j++)
                {
                    result += i * j;
                }
            }

            return result;
        }
    }
}

No comments:

Post a Comment