Chắc hẳn rằng hầu hết (100%) anh em ITer chúng ta đã dùng, đã tìm hiểu về lập trình không đồng bộ với Async và Await. Bài hôm này mình sẽ tóm tắt một số điểm cơ bản và đưa ra một ví dụ cụ thể để giải thích rõ ràng về nó hơn.
1. Lập trình không đồng bộ là gì?
Lập trình không đồng bộ với async và await là một phương pháp trong lập trình để xử lý các tác vụ không đồng bộ một cách dễ dàng và hiệu quả trong ngôn ngữ lập trình C#.
Khi một phương thức được đánh dấu với từ khóa async, nó có thể chứa các điểm chờ đợi (await) mà không làm đóng băng luồng chạy. Khi gặp từ khóa await, luồng chạy có thể chuyển sang thực hiện các tác vụ khác mà không cần chờ tác vụ hiện tại hoàn thành. Ngoài ra, khi tác vụ đang chờ đợi hoàn thành, luồng sẽ tiếp tục từ điểm chờ đợi mà không làm chương trình treo hoặc chặn.
Việc sử dụng async và await giúp làm cho code trở nên rõ ràng hơn và dễ hiểu hơn khi xử lý các tác vụ không đồng bộ, giúp tăng hiệu suất và tiết kiệm tài nguyên so với việc sử dụng các phương pháp truyền thống như sử dụng luồng hoặc callback.
Mình thấy trên ant cùng có một bài của anh Linh nói về Bất đồng bộ và song song, mọi người có thể tham khảo: https://ant.ncc.asia/dong-bo-bat-dong-bo-va-song-song/
2. Ví dụ
Mình sẽ sử dụng một ví dụ về hướng dẫn làm bữa sáng:
- Rót một cốc cà phê.
- Làm nóng một chảo, sau đó chiên hai quả trứng.
- Chiên ba lát thịt xông khói.
- Nướng hai miếng bánh mì.
- Thêm bơ và mứt vào bánh mì.
- Rót một cốc nước cam.
Nếu bạn có kinh nghiệm với việc nấu ăn, bạn sẽ thực hiện các hướng dẫn này không đồng bộ. Bạn có thể làm bữa sáng không đồng bộ bằng cách bắt đầu công việc tiếp theo trước khi công việc đầu tiên hoàn thành. Quá trình nấu tiến triển mà không cần ai đang theo dõi. Ngay khi bạn bắt đầu làm nóng chảo cho trứng, bạn có thể bắt đầu chiên thịt xông khói. Khi thịt xông khói bắt đầu, bạn có thể đặt bánh vào lò nướng.
2.1 Lập trình đồng bộ
Đối với một thuật toán song song, bạn sẽ cần nhiều đầu bếp (hoặc luồng). Một người sẽ làm trứng, một người sẽ chiên thịt xông khói, và cứ tiếp tục như vậy. Mỗi người sẽ tập trung chỉ vào một nhiệm vụ. Mỗi đầu bếp (hoặc luồng) sẽ bị chặn đồng bộ đợi thịt xông khói sẵn sàng để lật hoặc bánh mì nướng lên.
Cùng theo dõi đoạn code ở dưới đây:
using System;
using System.Threading.Tasks;
namespace AsyncBreakfast
{
internal class Bacon { }
internal class Coffee { }
internal class Egg { }
internal class Juice { }
internal class Toast { }
class Program
{
static void Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("Cà phê sẵn sàng");
Egg eggs = FryEggs(2);
Console.WriteLine("Trứng sẵn sàng");
Bacon bacon = FryBacon(3);
Console.WriteLine("Thịt xông khói sãn sàng");
Toast toast = ToastBread(2);
ApplyButter(toast);
ApplyJam(toast);
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!");
}
private static Juice PourOJ()
{
Console.WriteLine("Rót nước cam");
return new Juice();
}
private static void ApplyJam(Toast toast) =>
Console.WriteLine("Đang thêm mứt vào bánh mì");
private static void ApplyButter(Toast toast) =>
Console.WriteLine("Đang thêm bơ vào bánh mình");
private static Toast ToastBread(int slices)
{
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("Đang đặt một miếng bánh mì lên lò nướng.");
}
Console.WriteLine("Bắt đầu nướng");
Task.Delay(3000).Wait();
Console.WriteLine("Lấy bánh mì ra khỏi lò nướng.");
return new Toast();
}
private static Bacon FryBacon(int slices)
{
Console.WriteLine($"Đặt {slices} slices of bacon in the pan");
Console.WriteLine("Đang nấu một mặt ...");
Task.Delay(3000).Wait();
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("Lặt mặt thịt");
}
Console.WriteLine("Đang nấu mặt thứ 2");
Task.Delay(3000).Wait();
Console.WriteLine("Đặt thịt xông khói lên dĩa");
return new Bacon();
}
private static Egg FryEggs(int howMany)
{
Console.WriteLine("Đang làm nóng chảo ...");
Task.Delay(3000).Wait();
Console.WriteLine($"Đật{howMany} trứng");
Console.WriteLine("Đang nấu trứng ...");
Task.Delay(3000).Wait();
Console.WriteLine("Đặt trứng lên dĩa");
return new Egg();
}
private static Coffee PourCoffee()
{
Console.WriteLine("Rót cà phê");
return new Coffee();
}
}
}
Ta thấy rõ ràng, để làm một bữa sáng đơn giản phải thực hiện qua rất nhiều bước. Và Bữa sáng được chuẩn bị theo cách đồng bộ mất khoảng 30 phút vì tổng là tổng của mỗi nhiệm vụ.
Mời các bạn theo dõi phần sau để tìm hiểu về cách áp dụng Async Await vào ví dụ này
Tài liệu tham khảo: https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/