Back to results for “Performance”
C# › Asynchronous Programming

How does async/await work under the hood in C#, and what specifically happens when execution reaches an 'await'?

Advanced

Answer

The C# compiler transforms an async method into a compiler-generated state machine rather than executing it as ordinary sequential code -- when execution reaches 'await', the method pauses, control returns immediately to the caller, and the remaining logic is captured as a continuation that gets scheduled to run once the awaited task actually completes; critically, async/await does not by itself create a new thread, it just efficiently schedules continuations, with an actual new thread only introduced if code explicitly calls Task.Run(). Common pitfalls include calling .Result or .Wait() (forcing synchronous blocking on an async call, which can cause deadlocks in contexts with a synchronization context like ASP.NET or WPF) and forgetting ConfigureAwait(false) in library code, causing unnecessary context-switching overhead.
public async Task<int> FetchDataAsync() {
    await Task.Delay(1000);
    return 42;
}
// Conceptually compiles to something resembling:
public Task<int> FetchDataAsync() {
    var stateMachine = new FetchDataStateMachine();
    stateMachine.MoveNext(); // runs synchronously until the first await, then returns control
    return stateMachine.Task;
}
Real-world example A deadlock in a WPF application is traced to a UI event handler calling .Result on an async method, blocking the UI thread while the async continuation was itself waiting to resume on that exact same now-blocked UI thread, resolved by using await instead of .Result throughout the call chain.

Common follow-ups: Why specifically does calling .Result from a UI thread risk a deadlock that calling it from a console app's Main method usually doesn't?;What does ConfigureAwait(false) actually change about which thread a continuation resumes on?