6.8 — 6. Common Pitfalls
Mục tiêu bài học
- Nắm được ý chính của bài và mối liên hệ với module.
- Áp dụng được kiến thức vào bối cảnh CRM/.NET backend.
- Sẵn sàng chuyển sang bài kế tiếp với nền tảng chắc chắn.
Nội dung bài học
6.8.1 — Pitfall 1: Deadlock với .Result / .Wait()
// SAI — deadlock tiềm ẩn (đặc biệt trong ASP.NET Framework, WPF)
public string GetCustomerName(int id)
{
return GetCustomerAsync(id).Result; // DEADLOCK!
}
// ĐÚNG — async all the way
public async Task<string> GetCustomerNameAsync(int id)
{
var customer = await GetCustomerAsync(id);
return customer?.Name ?? string.Empty;
}
6.8.2 — Pitfall 2: Async over sync — khi nào dùng Task.Run?
// SAI — bọc sync I/O trong Task.Run không giúp ích gì
public async Task<string> ReadFileAsync(string path)
{
return await Task.Run(() => File.ReadAllText(path)); // Lãng phí thread pool
}
// ĐÚNG — dùng async I/O thực sự
public async Task<string> ReadFileAsync(string path, CancellationToken ct = default)
{
return await File.ReadAllTextAsync(path, ct);
}
// Task.Run CHỈ hợp lý cho CPU-bound trong background thread
public async Task<byte[]> CompressImageAsync(byte[] imageData)
{
// Tránh block UI/HTTP thread với CPU-bound work
return await Task.Run(() => ImageCompressor.Compress(imageData));
}
6.8.3 — Pitfall 3: Fire-and-forget đúng cách
// SAI — exception bị nuốt im, không trace được
public void LogActivity(int customerId)
{
_ = _logger.WriteActivityAsync(customerId); // Fire-and-forget không an toàn
}
// ĐÚNG — bắt exception, log, không crash app
public void LogActivityFireAndForget(int customerId)
{
_ = LogActivityInternalAsync(customerId);
}
private async Task LogActivityInternalAsync(int customerId)
{
try
{
await _logger.WriteActivityAsync(customerId);
}
catch (Exception ex)
{
_appLogger.LogError(ex, "Failed to log activity for customer {Id}", customerId);
}
}
6.8.4 — Pitfall 4: Exception trong Task.WhenAll
public async Task SendNotificationsAsync(NotificationRequest req, CancellationToken ct)
{
var emailTask = _email.SendAsync(req.Email, ct);
var smsTask = _sms.SendAsync(req.Phone, ct);
var pushTask = _push.SendAsync(req.DeviceToken, ct);
try
{
await Task.WhenAll(emailTask, smsTask, pushTask);
}
catch (Exception ex)
{
// WhenAll chỉ throw exception đầu tiên
// Kiểm tra tất cả tasks để lấy toàn bộ lỗi
var errors = new[] { emailTask, smsTask, pushTask }
.Where(t => t.IsFaulted)
.Select(t => t.Exception?.InnerException?.Message)
.ToList();
_logger.LogError(ex, "Notification errors: {Errors}", string.Join(", ", errors));
// Decide: throw hoặc partial success tùy business logic
}
}
Bài tập áp dụng
- Tóm tắt bài học bằng ngôn ngữ của bạn.
- Liên hệ nội dung với một tình huống thực tế trong dự án.
- Đề xuất một cải tiến cụ thể sau khi học bài này.
Tự kiểm tra
- Bạn có thể giải thích lại nội dung chính trong 2 phút không?
- Bạn có ví dụ áp dụng thực tế chưa?
- Bạn biết bước tiếp theo cần học/triển khai là gì không?
Kết luận
Hoàn thành bài này giúp bạn có góc nhìn đầy đủ hơn trước khi đi tiếp trong module.
Điều hướng
- Bài trước: 6.7 — 5. Async Patterns
- Bài tiếp theo: 6.9 — 7. Channels và Producer-Consumer
- Về module: Trang mục lục