await từng cái một là chạy tuần tự — tổng thời gian bằng tổng các lần chờ.
Muốn song song thì khởi động hết rồi mới await Task.WhenAll.
csharp
// sequential: ~300ms if each call takes 100ms
var a = await GetUserAsync(id);
var b = await GetOrdersAsync(id);
// parallel: ~100ms
var userTask = GetUserAsync(id);
var orderTask = GetOrdersAsync(id);
await Task.WhenAll(userTask, orderTask);
var user = await userTask; // already completed, just unwrapsVề exception: task do WhenAll trả về giữ AggregateException chứa tất cả lỗi, nhưng await chỉ ném lại exception đầu tiên. Nếu 2 task cùng hỏng, catch chỉ thấy 1 cái, cái còn lại biến mất khỏi log.
Muốn lấy đủ thì giữ biến task lại rồi đọc .Exception:
csharp
var all = Task.WhenAll(userTask, orderTask);
try { await all; }
catch { foreach (var e in all.Exception!.InnerExceptions) _logger.LogError(e, "sub-task failed"); }Lưu ý thêm: chỉ song song hoá khi các lời gọi thật sự độc lập, và không dùng chung một DbContext cho nhiều task song song — DbContext không thread-safe.