Async/Await Under Questioning
Async is the most reliably-asked C# topic, because most candidates use it daily and can't explain it. The rhythm is always the same: easy opener, then follow-ups that test how deep the mental model goes.
The opener and the first follow-up
"What does async/await actually do?" Headline: await doesn't block β it registers a
continuation on the incomplete Task, returns the thread to the pool, and resumes when the
awaited work finishes. The compiler rewrites your method into a state machine; async is
the keyword that authorizes that rewrite, not something that "makes it run on another thread."
First follow-up: "So does await block the current thread?" No β that's the whole
point. During an awaited database call, no thread is waiting; the thread serves other
requests. This is why async matters for a web server: it's about scalability under I/O,
not raw speed. A single awaited call isn't faster β the server just survives more concurrent
requests with the same thread pool.
Second follow-up: "Task vs thread?" A Task is a promise of a future result; a thread is an
execution resource. A Task may run on a pool thread (CPU work via Task.Run) or on no
thread at all (I/O waiting on the OS). Conflating them is the most common failure in this
round.
Sync-over-async β the standard scenario
"A legacy controller calls GetDataAsync().Result. What can go wrong?" Name it:
sync-over-async. It blocks a thread for the full duration, so under load you burn the
pool β and on classic .NET Framework with a SynchronizationContext, it's a textbook
deadlock: the blocked thread holds the context the continuation needs to resume on.
Then show the modern nuance: ASP.NET Core has no SynchronizationContext, so that specific
deadlock largely disappeared β but .Result is still wrong, because thread-pool starvation
under load remains. Knowing the deadlock is mostly historical while the practice is still
bad is exactly the depth marker this question exists to find. Related follow-up:
"ConfigureAwait(false) β do you still need it?" In Core app code it's a no-op habit; in
libraries it's still polite. Fix direction: make it async all the way up to the action.
Rapid-fire follow-ups worth pre-answering
- "
async void?" Only for event handlers β exceptions escape to the context and can crash the process; it can't be awaited. - "Run three independent calls concurrently?" Start all three, then
await Task.WhenAll(a, b, c);β awaiting each sequentially is the inefficiency often included in code-review questions. - "What's
CancellationTokenfor?" Cooperative cancellation β pass it through to EF and HTTP calls so an abandoned request stops costing you.
Practice prompts:
- Explain in 60 seconds why async improves throughput but not the latency of one request.
- "Walk me through what physically happens at the
awaitin an EF Core query." - Review this aloud: a loop that
awaits an independent HTTP call per item β what do you ask before parallelizing it? (Hint: is the downstream safe for concurrency?)