Here are some best practices on async/await programing model based on reading from:
- Async/Await – Best Practices in Asynchronous Programming
- TPL and async/await Best Practices for the Busy Developer
- Use ‘async Task’ or ‘async Task<T>’ instead of ‘async void’. Why? Primarily because of error-handling semantics. When using ‘async void’, any exception thrown will be raised directly on the SynchronizationContext that was active when the async void method started. When using ‘async Task’, exception is captured and plated on the Task object. The exception is asynchronous event handlers, which must regturn void (ex: ICommand.Execute implementations)
12345async Task MyMethodAsync(){// Do asynchronous work.await Task.Delay(1000);} - Async code works best if it goes all the way from bottom up (or top down). That means that you shouldn’t mix synchronous and asynchronous code without considering the consequences or blocking async code with .Wait or .Result. This can result in deadlocks depending on the type of the SynchronizationContext (GUI or ASP.NET). The second issue with Wait/Result is the complex error handling as exceptions are wrapped in AggregateException and thrown.
- Proper context configuration enables better parallelism as some asynchronous code can run in parallel with the GUI thread instead of concurrently on the same thread due to the SynchronizationContext. This also avoids deadlocks. However, you should not use ConfigureAwait(false) if code after the await needs the original context.
12345678910async Task MyAsyncMethod(){// Code here runs in the original context.await Task.Delay(1000);// Code here runs in the original context.await Task.Delay(1000).ConfigureAwait(continueOnCapturedContext: false);// Code here runs without the original// context (in this case, on the thread pool).}
Some tips:
Problem | Solution |
Create a task to execute code | Task.Run or TaskFactory.StartNew |
Create a task wrapper for an operation or event | TaskFactory.FromAsync or TaskCompletionSource<T> |
Support cancellation | CancellationTokenSource and CancellationToken |
Report progress | IProgress<T> and Progress<T> |
Handle streams of data | TPL Dataflow or Reactive Extensions |
Synchronize access to a shared resource | SemaphoreSlim |
Asynchronously initialize a resource | AsyncLazy<T> |
Async-ready producer/consumer structures | TPL Dataflow or AsyncCollection<T> |