If you've ever seen a request hang forever in a .NET application, no exception, no timeout, just... nothing.

There's a good chance somewhere in the call stack a developer wrote .Result or .Wait() on a task instead of awaiting it. This is one of the most common ways to introduce a deadlock.

ThreadPool Starvation

When you call .Result or .Wait(), the calling thread doesn't return to the pool, it sits there, doing nothing, until the awaited operation completes. Under light load this barely registers. Under real production traffic, it compounds:

// Called on every request, at scale
public IActionResult GetUsers()
{
    var users = _userService.GetUserssAsync().Result; // blocks a pool thread
    return Ok(users);
}

If your thread pool has, say, 50 threads available and every request blocks one of them while waiting on an I/O call, then under enough concurrent load you can exhaust the pool entirely.

New requests start queuing, waiting for a thread to free up, and since the pool grows slowly by design, the queue backs up faster than the pool can compensate. The application appears to hang, CPU usage looks suspiciously low (because threads are waiting, not computing), and response times spike or time out. This is thread pool starvation, and it's one of the more insidious production incidents because nothing in the code is technically broken.

The Fix: await All the Way Down

The rule is simple to state and easy to violate under deadline pressure: once you go async, stay async, all the way up the call stack.

public async Task<ActionResult> GetUser(int id)
{
    var user = await GetUserAsync(id); // ✅ releases the thread while waiting
    return View(user);
}

A Quick Way to Catch This in Review

.Result, .Wait(), and .GetAwaiter().GetResult() on a Task are worth treating as a code smell anywhere they appear outside of test setup. Analyzers like VSTHRD002 (part of the Microsoft.VisualStudio.Threading.Analyzers package) flag exactly this pattern and are worth adding to a CI pipeline if you don't already have something catching it.