An async method runs synchronously until it reaches an incomplete await. It then returns a Task and registers the remainder as a continuation. `await` does not itself create a thread. For true asynchronous I/O, no application thread needs to remain blocked while the operation is pending. ```csharp public async Task<Customer?> LoadAsync(int id, CancellationToken ct) { using var response = await http.GetAsync($"customers/{id}", ct); if (response.StatusCode == HttpStatusCode.NotFound) return null; response.EnsureSuccessStatusCode(); return await response.Content.ReadFromJsonAsync<Customer>(cancellationToken: ct); } ``` Thread-pool starvation occurs when work is queued but available worker threads are blocked, often by `.Result`, `.Wait()`, synchronous I/O, or long locks. Prefer async all the way and pass cancellation tokens. Classic UI or legacy ASP.NET deadlocks can occur when synchronous code blocks a context thread while the awaited continuation needs that same context. ASP.NET Core does not install that synchronization context, but blocking still harms scalability. Use `async void` only for event handlers; otherwise return `Task` so callers can await completion and observe exceptions.
Krishna Yadav · · 0 likes