Asynchronous programming – Async & Await – P3

3 min read

Hai bài trước ta đã tìm hiểu về lập trình đồng bộ và bất đồng bộ. Tuy nhiên, đoạn code lập trình bất động bộ trước chưa phải là tối ưu nhất. Chúng ta hãy cùng tìm cách để tối ưu đoạn code đấy.

2. Ví dụ

2.3 Tối ưu hóa lập trình bất đồng bộ

Bạn đã chuẩn bị mọi thứ cho bữa sáng cùng một lúc ngoại trừ bánh mì nướng. Làm bánh mì nướng là việc kết hợp của một hoạt động không đồng bộ (nướng bánh mì), và các hoạt động đồng bộ (thêm bơ và mứt). Cập nhật mã này minh họa một khái niệm quan trọng:

static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
{
    var toast = await ToastBreadAsync(number);
    ApplyButter(toast);
    ApplyJam(toast);

    return toast;
}

Trước khi phục vụ bữa sáng, bạn muốn đợi tác vụ đại diện cho việc nướng bánh mỳ trước khi thêm bơ và mứt. Bạn có thể biểu diễn công việc đó với mã sau đây:

static async Task Main(string[] args)
{
    Coffee cup = PourCoffee();
    Console.WriteLine("Cà phê sẵn sàng");

    var eggsTask = FryEggsAsync(2);
    var baconTask = FryBaconAsync(3);
    var toastTask = MakeToastWithButterAndJamAsync(2);

    var eggs = await eggsTask;
    Console.WriteLine("Trứng sẵn sàng");

    var bacon = await baconTask;
    Console.WriteLine("Thịt sẵn sàng");

    var toast = await toastTask;
    Console.WriteLine("Bánh mì nướng sẵn sàng");

    Juice oj = PourOJ();
    Console.WriteLine("Nước cam sẵn sàng");
    Console.WriteLine("Bữa sáng sẵn sàng!");
}


Await tasks efficiently

Các câu lệnh await cuối cùng trong đoạn code trước đó có thể được cải thiện bằng cách sử dụng các phương thức của lớp Task. Một trong những API đó là WhenAll, nó trả về một Task hoàn thành khi tất cả các tác vụ trong danh sách đối số của nó đã hoàn thành, như được thể hiện trong đoạn code sau:

await Task.WhenAll(eggsTask, baconTask, toastTask);
Console.WriteLine("Trứng sẵn sàng");
Console.WriteLine("Thịt sẵn sàng");
Console.WriteLine("Bánh mì nướng sẵn sàng");
Console.WriteLine("Bữa sáng sẵn sàng!");

Một lựa chọn khác là sử dụng phương thức WhenAny, nó trả về một Task<Task> hoàn thành khi bất kỳ trong số các đối số của nó hoàn thành. Bạn có thể đợi Task được trả về, biết rằng nó đã hoàn thành. Đoạn mã sau cho thấy cách bạn có thể sử dụng WhenAny để đợi tác vụ đầu tiên hoàn thành và sau đó xử lý kết quả của nó. Sau khi xử lý kết quả từ tác vụ đã hoàn thành, bạn loại bỏ tác vụ đã hoàn thành đó khỏi danh sách các tác vụ được truyền vào WhenAny.

var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
while (breakfastTasks.Count > 0)
{
    Task finishedTask = await Task.WhenAny(breakfastTasks);
    if (finishedTask == eggsTask)
    {
        Console.WriteLine("Trứng sẵn sàng");
    }
    else if (finishedTask == baconTask)
    {
        Console.WriteLine("Thịt sẵn sàng");
    }
    else if (finishedTask == toastTask)
    {
        Console.WriteLine("Bánh mì sẵn sàng");
    }
    await finishedTask;
    breakfastTasks.Remove(finishedTask);
}

Gần cuối đoạn code, bạn thấy dòng await finishedTask;. Dòng await Task.WhenAny không đợi tác vụ đã hoàn thành. Nó đợi Task được trả về bởi Task.WhenAny. Kết quả của Task.WhenAny là tác vụ đã hoàn thành (hoặc bị lỗi). Bạn nên đợi tác vụ đó một lần nữa, ngay cả khi bạn biết rằng nó đã hoàn thành chạy. Đó là cách bạn lấy kết quả của nó, hoặc đảm bảo rằng ngoại lệ gây ra lỗi được ném ra.

Phiên bản cuối cùng của bữa sáng được chuẩn bị bất đồng bộ mất khoảng 15 phút vì một số nhiệm vụ đã chạy đồng thời, và mã lệnh giám sát nhiều tác vụ cùng một lúc và chỉ thực hiện hành động khi cần thiết.

Phiên bản code cuối cùng này là bất đồng bộ. Nó phản ánh chính xác hơn cách một người sẽ nấu một bữa sáng. Các tính năng ngôn ngữ cho async và await cung cấp bản dịch mà mỗi người thực hiện để tuân thủ các hướng dẫn viết: bắt đầu các tác vụ khi bạn có thể và không chặn chờ các tác vụ hoàn thành.

Tài liệu tham khảo: https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/

Bài trước đó: https://ant.ncc.asia/asynchronous-programming-async-await-p2/

Avatar photo

Clean Code: Nguyên tắc viết hàm trong lập trình…

Trong quá trình phát triển phần mềm, việc viết mã nguồn dễ đọc, dễ hiểu là yếu tố then chốt để đảm bảo code...
Avatar photo Dat Tran Thanh
3 min read

Leave a Reply

Your email address will not be published. Required fields are marked *