C# Task.Wait() Deadlock Fix
In this tutorial, you'll learn about C# Task.Wait() Deadlock Fix. We cover key concepts, practical examples, and best practices.
Your application freezes when you call .Wait() or .Result on an async Task:
var data = FetchDataAsync().Result; // hangs forever
Task.Wait() and Task.Result block the calling thread while the async method tries to marshal back to the captured SynchronizationContext. The thread is blocked waiting for the Task, and the Task is waiting for the thread to be free — a classic deadlock.
Step-by-Step Fix
1. Use await instead of Wait() or Result
WRONG — blocking call causes deadlock in UI or ASP.NET contexts:
public string GetData()
{
var json = httpClient.GetStringAsync(url).Result;
return json;
}
RIGHT — use await all the way up:
public async Task<string> GetDataAsync()
{
var json = await httpClient.GetStringAsync(url);
return json;
}
Callers also await:
string data = await GetDataAsync();
2. Use ConfigureAwait(false) for library code
WRONG — library code that captures the context unnecessarily:
public async Task<string> ReadFileAsync()
{
using var stream = File.OpenRead("data.txt");
using var reader = new StreamReader(stream);
return await reader.ReadToEndAsync();
}
RIGHT — use ConfigureAwait(false) when you don't need the context:
public async Task<string> ReadFileAsync()
{
using var stream = File.OpenRead("data.txt");
using var reader = new StreamReader(stream);
return await reader.ReadToEndAsync().ConfigureAwait(false);
}
This prevents deadlocks when called from blocking code.
3. Block in console apps with GetAwaiter().GetResult()
When you absolutely must block (e.g., in a console app's Main method):
WRONG — using Wait() which wraps exceptions in AggregateException:
static void Main()
{
RunAsync().Wait();
}
RIGHT — use GetAwaiter().GetResult() for better exception handling:
static void Main()
{
RunAsync().GetAwaiter().GetResult();
}
Or use the async Main support (C# 7.1+):
static async Task Main()
{
await RunAsync();
}
4. Handle timeouts with Wait(TimeSpan)
WRONG — indefinite wait:
task.Wait();
RIGHT — use timeout and check the result:
if (task.Wait(TimeSpan.FromSeconds(5)))
{
Console.WriteLine(task.Result);
}
else
{
Console.WriteLine("Operation timed out");
}
Expected output: either the result or a timeout message after 5 seconds.
Prevention
- Use
async/awaitthroughout the call stack — never mix blocking and async. - Use
ConfigureAwait(false)in library code to avoid context capture. - For console apps, use
async Task Main(C# 7.1+). - Enable analyzer rule CS1998 to detect async methods that don't await.
- Never use
.Resultor.Wait()on tasks that haven't completed.
Common Mistakes with task wait
- Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
- Non-exhaustive pattern matches that compile with warnings then crash at runtime
- Misunderstanding that
Stringis[Char]with poor performance for large text operations
These mistakes appear frequently in real-world CSHARP code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro