.NET Developer interview question

How can async/await cause deadlocks in .NET?

A deadlock can occur when code blocks on Task.Result or Wait while the continuation needs the captured synchronization context. Keep the call chain async, await tasks, avoid sync-over-async, and do not hold locks across await.

Direct answer

What a strong answer covers

How it works

In environments with a synchronisation context, such as classic ASP.NET, WPF, or WinForms, an await captures the context and schedules its continuation back onto it. If the calling code then blocks that same context with .Result or .Wait, the continuation can never run and both sides wait forever. ASP.NET Core has no synchronisation context, so the classic deadlock disappears there, but sync-over-async still burns thread-pool threads and causes starvation under load. The remedy is async all the way: await everything, use ConfigureAwait(false) in library code, and never hold a lock across an await.

Example

A WPF button handler called LoadAsync().Result; LoadAsync awaited an HTTP call whose continuation needed the UI thread that was blocked in Result, freezing the application. Changing the handler to async void with await, and the library to ConfigureAwait(false), removed the freeze. In an ASP.NET Core API the same .Result did not deadlock but exhausted the thread pool at 200 concurrent requests.

Common mistakes

  • Calling .Result or .GetAwaiter().GetResult() from code that runs on a captured context.
  • Using lock around an await, which does not compile, and switching to a Monitor that then spans the await.
  • Assuming ASP.NET Core makes blocking on tasks harmless.

A useful answer structure

Answer from your own work.

Do not memorise a script: use only examples you can explain honestly in follow-up questions.

Practise this answer with MiraHint

Open the desktop workspace and rehearse a clear answer in your own words.

Download MiraHint
.NET Developer interview questions