Lập trình bất đồng bộ với async/await trong C# – Phần 11
1. Lập trình bất đồng bộ là gì?
Lập trình bất đồng bộ (Asynchronous Programming) giúp ứng dụng tiếp tục thực thi mà không bị chặn bởi các tác vụ mất thời gian, như:
✅ Đọc/ghi file
✅ Gọi API
✅ Truy vấn database
✅ Xử lý hình ảnh/video
💡 Ví dụ thực tế:
- Khi tải hình ảnh trong một ứng dụng, giao diện vẫn phản hồi thay vì bị đơ.
- Một ứng dụng web có thể tải dữ liệu từ server mà không làm gián đoạn người dùng.
2. Sự khác biệt giữa đồng bộ và bất đồng bộ
💢 Đồng bộ (Synchronous)
Từng dòng lệnh chạy tuần tự, lệnh sau phải chờ lệnh trước hoàn thành.
using System;
using System.Threading;
class Program
{
static void Main()
{
Console.WriteLine("Bắt đầu tải dữ liệu...");
Thread.Sleep(3000); // Giả lập thời gian tải
Console.WriteLine("Dữ liệu đã tải xong!");
}
}
⏳ Kết quả:
Bắt đầu tải dữ liệu...
(Sau 3 giây)
Dữ liệu đã tải xong!
⚠ Vấn đề: Chương trình bị chặn trong 3 giây.
🚀 Bất đồng bộ (Asynchronous)
Cho phép chương trình tiếp tục thực thi trong khi đợi tác vụ hoàn thành.
using System;
using System.Threading.Tasks;
class Program
{
static async Task DownloadDataAsync()
{
Console.WriteLine("Bắt đầu tải dữ liệu...");
await Task.Delay(3000); // Giả lập tải dữ liệu
Console.WriteLine("Dữ liệu đã tải xong!");
}
static async Task Main()
{
await DownloadDataAsync();
Console.WriteLine("Tiếp tục xử lý...");
}
}
⏳ Kết quả:
Bắt đầu tải dữ liệu...
(Sau 3 giây)
Dữ liệu đã tải xong!
Tiếp tục xử lý...
✅ Lợi ích: Trong khi chờ tải dữ liệu, chương trình có thể thực hiện các tác vụ khác.
3. Từ khóa async và await hoạt động như thế nào?
📌 async: Đánh dấu một phương thức là bất đồng bộ.
📌 await: Tạm dừng thực thi phương thức cho đến khi tác vụ hoàn thành mà không chặn chương trình.
Ví dụ:
static async Task SayHelloAsync()
{
Console.WriteLine("Xin chào!");
await Task.Delay(2000); // Chờ 2 giây
Console.WriteLine("Chào mừng bạn đến với C#!");
}
🔹 Khi gọi await Task.Delay(2000), chương trình có thể làm việc khác thay vì bị chặn.
4. Gọi nhiều tác vụ bất đồng bộ cùng lúc
Thay vì chờ từng tác vụ hoàn thành, ta có thể chạy chúng song song.
static async Task Task1()
{
await Task.Delay(2000);
Console.WriteLine("✅ Tác vụ 1 hoàn thành!");
}
static async Task Task2()
{
await Task.Delay(3000);
Console.WriteLine("✅ Tác vụ 2 hoàn thành!");
}
static async Task Main()
{
Task t1 = Task1();
Task t2 = Task2();
await Task.WhenAll(t1, t2); // Chờ cả hai tác vụ hoàn thành
Console.WriteLine("🎉 Tất cả tác vụ đã hoàn thành!");
}
⏳ Kết quả:
(Sau 2 giây) ✅ Tác vụ 1 hoàn thành!
(Sau 3 giây) ✅ Tác vụ 2 hoàn thành!
🎉 Tất cả tác vụ đã hoàn thành!
✅ Lợi ích: Chạy đồng thời nhiều tác vụ giúp tiết kiệm thời gian.
5. Xử lý lỗi trong async/await với try-catch
Bất đồng bộ vẫn có thể gặp lỗi, ta cần try-catch để xử lý.
static async Task DownloadDataAsync()
{
try
{
Console.WriteLine("Bắt đầu tải dữ liệu...");
await Task.Delay(2000);
throw new Exception("Lỗi kết nối!"); // Giả lập lỗi
}
catch (Exception ex)
{
Console.WriteLine($"❌ Lỗi: {ex.Message}");
}
}
static async Task Main()
{
await DownloadDataAsync();
Console.WriteLine("Chương trình tiếp tục chạy...");
}
⏳ Kết quả:
Bắt đầu tải dữ liệu...
(Sau 2 giây)
❌ Lỗi: Lỗi kết nối!
Chương trình tiếp tục chạy...
✅ Lợi ích: Dễ dàng kiểm soát và xử lý lỗi trong các tác vụ bất đồng bộ.
6. Tạo phương thức bất đồng bộ trả về giá trị
Ngoài Task, ta có thể trả về Task.
static async Task<int> GetDataAsync()
{
await Task.Delay(2000);
return 42;
}
static async Task Main()
{
int result = await GetDataAsync();
Console.WriteLine($"✅ Kết quả: {result}");
}
⏳ Kết quả:
(Sau 2 giây)
✅ Kết quả: 42
✅ Lợi ích: Giúp xử lý dữ liệu mà không chặn chương trình.
7. Khi nào KHÔNG nên dùng async/await?
❌ Khi tác vụ hoàn thành ngay lập tức (không có chờ đợi).
❌ Khi cần truy cập tài nguyên dùng chung (vì có thể gây lỗi đồng bộ).
❌ Khi hiệu suất quan trọng (vì async có một chút overhead).
8. Tóm tắt bài học
✅ Lập trình bất đồng bộ giúp chương trình không bị chặn khi xử lý tác vụ mất thời gian.
✅ Sử dụng async để khai báo phương thức bất đồng bộ.
✅ Dùng await để chờ tác vụ hoàn thành mà không chặn chương trình.
✅ Dùng Task.WhenAll() để chạy nhiều tác vụ cùng lúc.
✅ Dùng try-catch để xử lý lỗi trong async/await.
💡 Bài tiếp theo:
👉 Lập trình mạng với HTTPClient trong C# – Phần 12 🚀