async and await (Part 3 of 4)

We’re busy with a journey of discovering the basics of writing good asynchronous code. Here’s a quick road map:

In Part 2, we explored the essentials of TPL. In .Net 4.5 we’ve been presented with a new kids around the block async & await. The huge advantage of these 2 is that they even further simplify the code and enable you to run logic asynchronously on a single thread. Yip, re-read that sentence again. It blew my mind the first time I did. This is a huge thing when it comes to writing non-blocking UI code.

Let’s test these magic keywords:

We’ll write a little WPF application with 3 TextBoxes. Then on the form load, we do 3 different “heavy calculations” and place each result into a TextBox. We’ll use TPL basics we’ve learned up until now.

Xaml:


<StackPanel>
   <TextBox x:Name="txt1" />
   <TextBox x:Name="txt2" />
   <TextBox x:Name="txt3" />
</StackPanel>

Here’s our TPL code behind:


private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
   var t1 = HeavyCalculation(2000000000);
   var t2 = HeavyCalculation(1800000000);
   var t3 = HeavyCalculation(int.MaxValue);

   txt1.Text = t1.Result.ToString();
   txt2.Text = t2.Result.ToString();
   txt3.Text = t3.Result.ToString();
}

public Task<string> HeavyCalculation(int number)
{
   return Task.Run<int>(() =>
   {
      int total = 0;
      for (int i = 0; i < number; i++)
      {
         unchecked { total += i; }
      }
      return total.ToString();
   });
}

The above works, The 3 tasks run asynchronously and the main thread carries on. If we look at lines 7, 8, 9 we set our TextBoxes to our Task results. This blocks the Main Thread until the results are obtained, resulting in our UI Thread hanging / freezing for a little bit. See image below of UI “freezing”:

wpf_freeze

Don’t get me wrong, this is already loads better than calling all 3 of our methods synchronously. We can make this a much better user experience using async & await? Let’s see how we would do that:


private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
   var t1 = HeavyCalculation(2000000000);
   var t2 = HeavyCalculation(1800000000);
   var t3 = HeavyCalculation(int.MaxValue);

   txt1.Text = await t1;
   txt2.Text = await t2;
   txt3.Text = await t3;
}

public async Task<string> HeavyCalculation(int number)
{
   return await Task.Run(() =>
   {
      int total = 0;
      for (int i = 0; i < number; i++)
      {
         unchecked { total += i; }
      }
      return total.ToString();
   });
}

  • In the above code we added async keyword to the methods (this simply enables the await keyword in the method).
  • Line 14 we added the await keyword.
  • Now in lines 7, 8, 9 instead of explicitly asking for the results, (t1.Result), we use the await keyword.
  • The UI is completely responsive and as the results “come in”, the TextBox values will be set.

See the screenshots below:

wpf_async1 wpf_async2 wpf_done

Immediately the UI is responsive and the user can interact with our system even though our results aren’t all ready yet. As the results come in, the TextBoxes get populated.

How does it do this?

(Using the above example as a reference)

  • The Main Thread kicks off the 3 tasks asynchronously as before.
  • Now the UI Thread continues until it reaches line 7.
  • In the first example (t1.Result) the UI Thread blocked until the result is available.
  • Using await, instead of blocking the UI thread, the method returns control to the caller and the UI becomes responsive.
  • Now the “message pump” / “message loop” will continue to check the status of the Task. (how the message loop works is out of the scope of this article).
  • Once the result comes in, the Task object is marked as completed.
  • The message loop will give control back to the await at line 7 and continues until line 8.
  • If the result is ready, the result will be returned, otherwise, control is sent back to the caller (UI Thread) until a result comes in. This is how the UI becomes responsive.

Something important to note is that by default the await “saves” the SynchronizationContext of the function calling it. This can cause deadlocks .Result or .Wait and await are used together. (See async pitfalls, Part 4)

Error handling

Exception handling using async and await is much simpler as well. We can simply use a try … catch statement as if we were writing synchronous code:



try
{
   int result = await GetResult();
}
catch(Exception)
{
   // Handle exception...
}

What’s Next?

That’s it for covering the basics of async and await. In Part 4, we will look at some of the common pitfalls of asynchronous programming.

Advertisements

4 thoughts on “async and await (Part 3 of 4)

  1. Hi Niels,

    As you are well aware, async/await cannot be used on a constructor of a class – for obvious reasons. Which brings me to my question. Let’s say you are making use of a MVVM pattern and your view model needs to get data from a Restful service when it is constructed. Seeing that the constructor is the entry point to our class and taking into consideration that we can’t use async/await, what would you suggest doing in such a scenario? Take the following example into consideration:

    public class PersonViewModel
    {
    // this will not compile because of async
    public async PersonViewModel(IDataService dataService)
    {
    // this will not compile because of await in CTOR
    var personData = await dataService.GetPersonData();
    // now we want to make use of the person data
    // but execution will pass immediately over GetPersonData ??
    if (personData != null)
    {
    // do something useful with the person data
    }
    }
    }

    Liked by 1 person

    • Hi Richard,

      Thanks for the comment. This is a scenario I haven’t run into before, so my answer is based on the approach I would take and not from experience. Constructors cannot be async as you mentioned since async methods need a return type of Task or even void, which is not possible in a constructor.

      I would create an interface for ViewModels that need to be initialized asynchronously and then create a construct method which will handle the asynchronous construction. This method would accept IDataService interface and any other overloads which constructors would use.

      public interface IViewModelAsyncIntializer
      {
      Task ConstructAsync(IDataService dataService);
      }

      public class PersonViewModel : IViewModelAsyncIntializer
      {
      public PersonViewModel() { }

      public async Task ConstructAsync(IDataService dataService)
      {
      var personData = await dataService.GetPersonData();
      if (personData != null)
      {
      // do something useful with the person data
      }
      }
      }

      Then instantiating the ViewModel you simply call the ConstructAsync after normal construction:

      IViewModelAsyncIntializer vmPerson = new PersonViewModel();
      vmPerson.IntializeAsync(myDataServiceInstance);

      Having to remember to call construct method after instantiating your ViewModel might not be an acceptable approach to some, because forgetting to call it would mean that you use an uninitialized ViewModel. If you’re using a ViewModel-First approach, then you’re likely to have a “common” construction method which creates ViewModels and navigates to them. Then the above is not a problem as you would implement this in the common method. Alternatively, you can always do a check before implementing any work that requires the ViewModel construct to have completed.

      Like

  2. Thanks Niels! I’ve made use of this helper in the past to get around this issue, but I’ll keep you suggestion in mind.

    /// <summary>
    /// Loads the data in an asynchronous manner.
    /// </summary>
    /// <typeparam name="T">The specified type that is expected when callback is called as well as the loaded func.</typeparam>
    /// <param name="callback">The callback.</param>
    /// <param name="loader">The loader.</param>
    public static void LoadData<T>(Action<T> callback, Func<Task<T>> loader)
    {
    var task = loader();
    var awaiter = task.GetAwaiter();
    awaiter.OnCompleted(() =>
    {
    if (task.Exception != null)
    {
    throw task.Exception;
    }
    callback(task.Result);
    });
    }

    Like

  3. Thanks Richard, good to know. I guess the concept with the helper is the same, the loader delegate being the “constructor” method of a class that needs to be called. The great thing about your LoadData method which I like is you’re not bound to “specific construction overloads”, since you can put anything into your loader delegate as long as it returns Task<T>: () => MyOverloaddedMethod(“lots of overloads”, true, 10, “more”)

    Thanks again for sharing.

    Like

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