That Conference 2018 – Correcting Common Mistakes When Using async/await in .NET

That Conference 2018, Kalahari Resort, Lake Delton, WI
Correcting Common Mistakes When Using async/await in .NET – Brandon Minnick

Day 1, 6 Aug 2018  4:00 PM

Disclaimer: This post contains my own thoughts and notes based on attending That Conference 2018 presentations. Some content maps directly to what was originally presented. Other content is paraphrased or represents my own thoughts and opinions and should not be construed as reflecting the opinion of the speakers.

Brandon Minnick

Developer Advocate, Microsoft

@TheCodeTraveler

https://www.codetraveler.io/THAT-2018-AsyncAwait/

Executive Summary

  • Helps to know underlying IL that’s generated
  • Helps to know core task-based objects that async/await use
  • ConfigureAwait(false) if you don’t need to come back to UI thread
  • .GetAwaiter().GetResult() instead of await to get synchronous execution

Multi Threading

  • e.g. ReadDataFromUrl
    • Our method is async
    • does await on DownloadDataTaskAsync
    • 1st chunk of code is run by the thread that calls this method
    • 2nd thread–calls the Download method
    • After await, execution picks up with 1st thread
  • Typically, first thread, i.e. main thread, is the UI thread
  • UI thread
    • Draws UI on screen
    • Rule-of-thumb for executing on UI thread. Because refresh rate is 60Hz, don’t take more then 1/60 sec (17 ms)

Intermediate Language

  • Compiler generates class for your original method
  • Implements IAsyncStateMachine
  • Every local variable from method becomes private field in class
  • MoveNext method
    • Giant switch statement
    • $PC keeping track of state
    • Also a big try/catch block surrounding everything

Quick Review

  • async keyword adds 100 bytes
  • Every async method becomes a class

Await Every Async Method

  • Non-awaited async methods hide exceptions
  • Ditto for Task.Run — GetAwaiter().GetResult() will ensure we catch exception

Let’s Fix Some Code

  • Can’t await in a constructor
    • ICommand–made for fire and foreget
    • In Execute method, do async lambda with await
    • So from constructor, we can call MyCommand.Execute(null)
  • Never use async void ?
    • Not entirely true
    • Don’t use async void when not on UI thread
    • But on UI thread, if you throw exception from within async void–you’ll catch the exception
  • So from constructor, call Initialize(), which is an async void and can do the await
  • NEVER use .Wait
    • Blocks calling thread entirely
    • Doesn’t unwrap an exception
  • NEVER use .Result
  • Way better method
    • GetAwaiter().GetResult()
    • Does unwrap any exceptions
    • Can get the result from the async method
  • Note: .GetAwaiter().GetResult() is to be used in non-async method
    • In async method, just use await keyword
  • ConfigureAwait(false)
    • By default, you context switch back to UI thread
    • But this is expensive–background thread has to wait for UI thread
    • Context switch takes time
    • When background thread is done, execution stays on that background thread. Does not come back to UI thread
    • But note that you do need to come basck to UI thread if that method does some stuff with the UI, e.g. modify a control
  • ConfigureAwait(true)
    • This is the default, equivalent to not having ConfigureAwait() there at all
  • Don’t call .Result
    • Replace with await that method call to get the result directly
  • Special case, returning await something that returns Task<>
    • You can return the task directly, rather than awaiting it, then doing async on the method
    • Look for “return await”
  • Exception to this rule
    • You should await if you want to catch exception in method that you’re calling
    • So you could argue not to every do that trick

Async/Await Best Practices

  • Never use .Wait or .Result
    • Use await keyword
    • Or .GetAwaiter().GetResult()
  • Fire and forget tasks
    • Use ICommand
    • (or) async void (if you know you’re going to be on main thread)
  • Avoid return await
    • Remove async keyword
    • Except in try/catch blocks or using blocks
  • Utilize ConfigureAwait(false) as much as possible
    • In methods that don’t touch the UI

Xamarin University

  • Various course, e.g. CSC350: Using async await

Leave a comment