A couple of years ago I posted a second version of recommended reading for developers. At the time I was training up my team on some of the newer development features, namely the new async support in .Net. Since then I have come across two pitfalls that can catch developers off guard.
For some background, we need to understand what the compiler does when it processes the async and await keywords. The async keyword only affects the local method, not its callers or callee's. The compiler adds a state machine into the method so that it can track what happens before and after hitting an awaited task. For example, all variables (and parameters) declared before the awaited task must be stored so that the continuation has access to them. The continuation is the code that executes after the awaited task. The continuation may or may not execute on the thread that originally invoked the method.
One thing to note here is that using the async keyword on a method that doesn't have a continuation adds a performance hit because the compiler is adding an unnecessary state machine into the method. There would be no continuation either because there is no awaited task or no code blocks after the only awaited task. To avoid the performance hit of the state machine, a simple pass-through style method should not use async.