🌟 async và await trong C# – Làm sao để sử dụng đúng? 🤔
4 phút đọc

async và await trong C# – Làm sao để sử dụng đúng? 🤔
🔹 Bạn đã bao giờ gặp tình huống code chạy nhưng performance không như mong đợi?
🔹 Bạn có bao giờ phân vân liệu mình đang sử dụng async và await một cách tối ưu?
Hôm nay, mình muốn chia sẻ một số điểm quan trọng về lập trình bất đồng bộ (asynchronous programming) trong C# mà nhiều lập trình viên thường bỏ qua. 🚀
1. async và await thực sự là gì?
Khi khai báo một phương thức async, chúng ta đang cho trình biên dịch của C# biết rằng phương thức này hỗ trợ cơ chế bất đồng bộ (không block main thread). Từ khóa await cho phép chờ một task hoàn thành mà không làm "treo" ứng dụng.
📌 Ví dụ đơn giản:
public async Task<string> GetDataAsync()
{
await Task.Delay(2000); // Giả lập độ trễ 2 giây
return "Dữ liệu đã sẵn sàng!";
}
Lệnh await Task.Delay(2000); giúp phương thức tiếp tục thực thi một cách bất đồng bộ thay vì chặn luồng chính.
2. Những sai lầm phổ biến khi dùng async và await
❌ Sai lầm 1: Quên await khi gọi phương thức async
var data = GetDataAsync(); // Không có await!
Console.WriteLine(data.Result); // Có thể gây deadlock 😱
✅ Cách đúng:
var data = await GetDataAsync();
Console.WriteLine(data);
❌ Sai lầm 2: Dùng .Result hoặc .Wait() trên Task
Một sai lầm nữa là khi làm việc với các phương thức async, chúng ta thường cố gắng lấy kết quả ngay lập tức bằng cách sử dụng .Result hoặc .Wait(). Tuy nhiên, điều này có thể gây deadlock hoặc làm giảm performance của ứng dụng.
📌 Ví dụ sai:
public async Task<string> GetDataAsync()
{
await Task.Delay(2000); // Giả lập một tác vụ bất đồng bộ
return "Dữ liệu đã sẵn sàng!";
}
public void SomeMethod()
{
var data = GetDataAsync().Result; // Có thể gây deadlock 😱
Console.WriteLine(data);
}
❌ Tại sao sai?
- .Result hoặc .Wati() chặn luồng chính và đợi task hoàn thành, làm mất đi lợi ích của async/await.
- Nếu gọi từ UI thread (trong WinForms/WPF), nó có thể gây deadlock vì UI thread đang đợi kết quả, trong khi Task lại đang chờ thread này trống để hoàn thành.
✅ Cách đúng: Hãy luôn sử dụng await để tránh block thread.
public async void SomeMethodAsync()
{
var data = await GetDataAsync();
Console.WriteLine(data);
}
❌ Sai lầm 3: Sử dụng Task.Run() một cách không cần thiết
Chúng ta thường cho rằng chỉ cần đưa một phương thức đồng bộ vào Task.Run() là cách tốt để biến nó thành bất đồng bộ. Tuy nhiên, đây là một hiểu lầm vì Task.Run() chỉ giúp chuyển tác vụ sang một thread khác, chứ không làm cho code chạy hiệu quả hơn.
📌 Ví dụ sai:
public string GetData()
{
Thread.Sleep(2000); // Giả lập độ trễ
return "Dữ liệu đã sẵn sàng!";
}
public async Task<string> GetDataAsync()
{
return await Task.Run(() => GetData()); // Không cần thiết!
}
❌ Tại sao sai?
- Không cần thiết vì việc I/O-bound như đọc file, gọi API hay truy vấn database đã có sẵn hỗ trợ async (ví dụ: HttpClient.GetStringAsync(), File.ReadAllTextAsync()...).
- Task.Run() hữu ích khi chạy CPU-bound work (tác vụ tính toán nặng), nhưng không nên dùng cho I/O.
- Khi dùng Task.Run(), chúng ta vẫn dùng một thread từ thread pool, làm tiêu tốn tài nguyên mà không thực sự cần thiết.
✅ Cách đúng: Nếu tác vụ hỗ trợ async, hãy dùng async một cách tự nhiên thay vì Task.Run().
public async Task<string> GetDataAsync()
{
await Task.Delay(2000); // Giả lập độ trễ
return "Dữ liệu đã sẵn sàng!";
}
🔹 Khi nào nên dùng Task.Run()?
- Khi xử lý các tác vụ CPU-bound như mã hóa dữ liệu, xử lý hình ảnh, hoặc tính toán lớn.
- Khi muốn chạy một tác vụ đồng bộ mà không muốn chặn thread chính (nhưng vẫn nên cân nhắc thiết kế tốt hơn).
✅ Ví dụ hợp lý:
public async Task<int> ComputeHeavyWorkAsync()
{
return await Task.Run(() =>
{
// Giả lập một tác vụ tính toán nặng
int result = 0;
for (int i = 0; i < 1000000; i++)
result += i;
return result;
});
}
3. Khi nào nên dùng async và await?
✅ Khi gọi API, đọc/ghi file, truy vấn database hoặc thực hiện các tác vụ I/O.
✅ Khi muốn giữ giao diện UI luôn phản hồi tốt trong ứng dụng WinForms/WPF.
✅ Khi xử lý tác vụ dài hơi mà không muốn chặn main thread.
⚡ Kết luận: Việc hiểu rõ async và await giúp bạn viết code hiệu suất cao hơn, tránh lỗi phổ biến và tối ưu hóa ứng dụng. Bạn đã từng gặp lỗi nào khi dùng async/await chưa? Hãy chia sẻ kinh nghiệm của bạn nhé! 🚀🔥
Nguồn: Minh Phương Dương