Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Chạy nhiều task bất đồng bộ và chờ hoàn tất trong C#

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ứcBlocking?Trả vềDùng khi
Task.WaitAllCó (block thread hiện tại)voidCode đồng bộ (Main, legacy, test)
Task.WhenAllKhô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.