Async pitfalls

1 min read

Avoid async void

Prefer async Task. Methods marked async void should generally be avoided, except for event handlers. They have different error-handling semantics (uncaught exceptions can terminate the process), and they are harder to test because they don’t return a Task to await. The full reasoning is in why async void is dangerousWhy async void is dangerousIt discards the Task, so the caller cannot await it or catch its exceptions.Opens in a new tab.

Use ConfigureAwait(false) in libraries

In general-purpose library code, use ConfigureAwait(false) on awaited tasks. This stops the continuation from trying to resume on the original synchronization context, such as the UI thread or the ASP.NET Core request context. That helps avoid deadlocksDeadlocksTwo or more threads wait on each other forever.Opens in a new tab, especially when the library is used in many kinds of application.

C#
await myTask.ConfigureAwait(false);

Application-level code, like UI event handlers or ASP.NET Core controller actions, might need the original context and can leave ConfigureAwait(false) out.

Understand scalability versus parallelism

As in the first note of this chapterAsync and awaitAwait frees the thread while an operation waits, and the method picks up where it left off.Opens in a new tab, async/await mainly improves scalability for I/O-bound work by improving thread utilization. It doesn’t parallelize tasks by itself.

Propagate asynchrony

If you call an async method, you should generally await it and make the calling method async as well: go “async all the way up”. Blocking on async code, for example with .Result or .Wait(), can lead to deadlocksDeadlocksTwo or more threads wait on each other forever.Opens in a new tab. Blocking also holds a pool thread hostage, as Thread.Sleep versus Task.DelayThread.Sleep versus Task.DelaySleep blocks the thread. Delay frees it.Opens in a new tab shows.


Sources