async/await does not “release the thread”

There is some language around async/await that I am going to stop using. I’ve heard others use it as well because it does help get the point across but I believe it is ultimately misleading. Async/await does not “release the thread.”

To see this you need to look at one level higher in your call stack than where you are awaiting. Consider the following code

  1: private void Button_Click(object sender, RoutedEventArgs e)
  2: {
  3:     LoadData();
  4: }
  5:  
  6: private async Task LoadData()
  7: {
  8:     HttpClient client = new HttpClient();
  9:     string result1 = await client.GetStringAsync(new Uri("https://www.microsoft.com/)"));
  10:     int count = result1.Length;
  11:     string result2 = await client.GetStringAsync(new Uri("https://msdn.microsoft.com"));
  12:     count = result1.Length + result2.Length;
  13:  
  14:     MessageDialog messageDialog = new MessageDialog("Found " + count.ToString() + " characters");
  15:     await messageDialog.ShowAsync();
  16: }

When people hear “release the thread” as in “when the code gets to the first ‘await’ on line 9, it releases the thread so that the UI can run while the async method happens” it gives the wrong impression that the thread is instantly put back into the thread pool (or something) and is available for arbitrary work to be assigned it.

The next thing it does is not arbitrary. When the execution gets to the await, the async operation is started and the thread returns to the immediate caller. In this case the caller is Button_Click which itself just returns. So no harm no foul right? Wrong. Lets make a minor change.

  1: private void Button_Click(object sender, RoutedEventArgs e)
  2: {
  3:     LoadData();
  4:     DoSomething();
  5:     DoSomethingElse();
  6: }
  7:  
  8: private async Task LoadData()
  9: {
  10:     HttpClient client = new HttpClient();
  11:     string result1 = await client.GetStringAsync(new Uri("https://www.microsoft.com/)"));
  12:     int count = result1.Length;
  13:     string result2 = await client.GetStringAsync(new Uri("https://msdn.microsoft.com"));
  14:     count = result1.Length + result2.Length;
  15:  
  16:     MessageDialog messageDialog = new MessageDialog("Found " + count.ToString() + " characters");
  17:     await messageDialog.ShowAsync();
  18: }

We haven't changed the method of LoadData, our async method. So it still works as planned. What we changed was the calling method. When the code gets to line 11 and hits the first await, the async operation is started and the thread returns to the caller. In this case we have added code after our call to our async method. That code will execute immediately on the same thread. So while GetStringAsync is executing, DoSomething will execute and then DoSomethingElse. Maybe that’s OK and maybe its not. Just don’t assume that the thread disappears and magically reappears later.

The thread doesn’t go away, it just returns and keeps on truckin’

AsyncAwaitNotReleaseThread.zip