C# cung cấp hai cách chính để chờ nhiều Task hoàn thành: Task.WaitAll (blocking) và Task.WhenAll (non-blocking, awaitable).
So sánh nhanh
| Phương thức | Blocking? | Trả về | Dùng khi |
|---|---|---|---|
Task.WaitAll | Có (block thread hiện tại) | void | Code đồng bộ (Main, legacy, test) |
Task.WhenAll | Không (trả về Task) | Task / Task<T[]> | Code async/await (khuyến nghị) |
Ví dụ 1: Task.WhenAll (Async - Khuyến nghị)
static async Task Main()
{
var task1 = Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Task 1 - iteration {i}");
await Task.Delay(1000);
}
Console.WriteLine("Task 1 complete");
});
var task2 = Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Task 2 - iteration {i}");
await Task.Delay(1000);
}
Console.WriteLine("Task 2 complete");
});
Console.WriteLine("Waiting for tasks to complete (WhenAll)...");
await Task.WhenAll(task1, task2); // Non-blocking await
Console.WriteLine("Both Tasks Completed.");
}
Ví dụ 2: Task.WaitAll (Sync - Blocking)
static void Main()
{
var task1 = Task.Run(() =>
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Task 1 - iteration {i}");
Thread.Sleep(1000); // Blocking sleep
}
Console.WriteLine("Task 1 complete");
});
var task2 = Task.Run(() =>
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Task 2 - iteration {i}");
Thread.Sleep(1000);
}
Console.WriteLine("Task 2 complete");
});
Console.WriteLine("Waiting for tasks to complete (WaitAll)...");
Task.WaitAll(task1, task2); // Block thread hiện tại
Console.WriteLine("Both Tasks Completed.");
}
Xử lý Exception
// WhenAll: AggregateException gói tất cả exception
try
{
await Task.WhenAll(task1, task2);
}
catch (Exception ex) when (ex is AggregateException agg)
{
foreach (var e in agg.InnerExceptions)
Console.WriteLine($"Error: {e.Message}");
}
// WaitAll: Ném AggregateException ngay lập tức
try
{
Task.WaitAll(task1, task2);
}
catch (AggregateException agg)
{
foreach (var e in agg.InnerExceptions)
Console.WriteLine($"Error: {e.Message}");
}
Kết hợp với kết quả trả về
var tasks = new[]
{
FetchDataAsync(1),
FetchDataAsync(2),
FetchDataAsync(3)
};
var results = await Task.WhenAll(tasks); // results là T[]
foreach (var r in results) Console.WriteLine(r);
Best practice: Luôn dùng await Task.WhenAll trong code async. Tránh Task.WaitAll / .Result / .Wait() trong ASP.NET Core để tránh deadlock.