CodeGym /Các khóa học /C# SELF /Xử lý lỗi trong các phép toán bất đồng bộ (

Xử lý lỗi trong các phép toán bất đồng bộ ( async/ await)

C# SELF
Mức độ , Bài học
Có sẵn

1. Giới thiệu

Bắt đầu bằng câu hỏi quan trọng: tại sao không thể cứ tin tưởng các method bất đồng bộ và hy vọng mọi thứ luôn chạy ổn? Các thao tác với file thường ném ngoại lệ — file có thể bị xóa, hết bộ nhớ, thiếu quyền hoặc file bị khóa. Trong code đồng bộ bạn sẽ bắt lỗi trong khối try-catch quen thuộc. Trong code bất đồng bộ triết lý giống nhau, nhưng có điểm khác: lỗi có thể xảy ra không ngay khi gọi method mà sau đó, khi thao tác thực sự được thực hiện.

Thời điểm phát sinh lỗi

Trong code đồng bộ khi đọc bằng StreamReader.Read() ngoại lệ sẽ bay ra ngay tại dòng gọi — bắt trong catch, xong.
Trong code bất đồng bộ (await stream.ReadAsync()) lỗi sẽ rơi không phải khi bắt đầu thao tác, mà chính tại await — khi task hoàn thành với lỗi. Nếu quên đặt await, lỗi có thể «vô hình» trong một thời gian.

2. Cách bắt ngoại lệ trong các method bất đồng bộ

Ta xem ngay mẫu tiêu biểu:


try
{
    using FileStream fs = new FileStream("myfile.txt", FileMode.Open);
    byte[] buffer = new byte[1024];
    int bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length);
    // Xử lý tiếp...
}
catch (IOException ex)
{
    Console.WriteLine("Lỗi I/O: " + ex.Message);
}
catch (UnauthorizedAccessException ex)
{
    Console.WriteLine("Không có quyền truy cập file: " + ex.Message);
}
catch (Exception ex)
{
    Console.WriteLine("Lỗi không xác định: " + ex);
}

Đúng rồi, đơn giản — dùng try-catch quen thuộc, nhưng trong method bất đồng bộ. Quan trọng là method đó phải có từ khóa async, nếu không compiler sẽ khó chịu.

Ghi nhớ: đặt await ở đâu


Task<int> readTask = fs.ReadAsync(buffer, 0, buffer.Length);
// ... ở đây vô tình quên await hoặc xử lý

Trong trường hợp này nếu có lỗi, nó sẽ nằm trong chính task (Task), và bạn sẽ không biết cho đến khi cố lấy kết quả — ví dụ bằng await readTask hoặc qua thuộc tính Task.Exception. Nếu hoàn toàn quên await — task có thể kết thúc với lỗi mà không ai báo cho bạn.

3. Tại sao code bất đồng bộ nguy hiểm nếu không xử lý lỗi

Tình huống 1: «Fire and forget» — bẫy cho người mới


FileStream fs = new FileStream("file.txt", FileMode.Open);
byte[] buffer = new byte[8000];
fs.ReadAsync(buffer, 0, buffer.Length);
// Và chương trình chạy tiếp cuộc sống của nó

Phép đọc chạy «đơn độc», và nếu nó hoàn thành với lỗi, không có catch nào bắt được. Ngoại lệ bị giấu trong task. Pattern này gọi là «fire and forget» và trong ứng dụng thực tế dễ dẫn tới mất lỗi quan trọng và rò rỉ tài nguyên.

Tình huống 2: Method bất đồng bộ không có await


Task t = MyAsyncMethod();
// ... làm gì đó, rồi quên mất t

Các lỗi trong MyAsyncMethod sẽ không nổi lên cho tới khi bạn chờ task rõ ràng (await t hoặc t.Wait()).

4. Cách đúng để bắt lỗi từ các task bất đồng bộ

Chiến lược 1: Luôn dùng await


try
{
    await SomeFileOperationAsync();
}
catch (Exception ex)
{
    Console.WriteLine("Có gì đó không ổn: " + ex.Message);
}

Như vậy ngoại lệ sẽ được ném ngay tại chỗ chờ và không bị mất.

Chiến lược 2: Xử lý bằng .ContinueWith

Nếu vì lý do nào đó bạn không dùng await, có thể thêm handler lỗi bằng ContinueWith:


var task = fs.ReadAsync(buffer, 0, buffer.Length);
task.ContinueWith(t =>
{
    if (t.Exception != null)
        Console.WriteLine("Lỗi khi đọc bất đồng bộ: " + t.Exception.InnerException);
}, TaskContinuationOptions.OnlyOnFaulted);

Thật lòng? Trong ứng dụng C# hiện đại người ta hiếm khi làm vậy — async/await làm code đơn giản và sạch hơn.

Những ngoại lệ có thể gặp khi làm việc với file bất đồng bộ

5. Ví dụ: Đọc bất đồng bộ với xử lý lỗi

Thêm logic này vào ứng dụng của chúng ta:


using System;
using System.IO;
using System.Threading.Tasks;

namespace FileAsyncDemo
{
    class Program
    {
        static async Task Main()
        {
            string path = "bigfile.txt";
            byte[] buffer = new byte[4096];

            try
            {
                using FileStream fs = new FileStream(path, FileMode.Open);
                int bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length);
                Console.WriteLine($"Đã đọc {bytesRead} byte từ {path}");
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine("Không tìm thấy file: " + ex.Message);
            }
            catch (UnauthorizedAccessException ex)
            {
                Console.WriteLine("Không có quyền truy cập file: " + ex.Message);
            }
            catch (IOException ex)
            {
                Console.WriteLine("Lỗi đọc/ghi: " + ex.Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Lỗi khác: " + ex.Message);
            }
        }
    }
}

Lưu ý quan trọng

Nếu bạn không dùng await trong Main, mà chỉ làm Task result = SomeAsyncMethod(); — các lỗi sẽ «im lặng» và chỉ hiện ra sau này khi bạn cố lấy kết quả.

6. Những lỗi thường gặp và "vấp" khi xử lý lỗi

Quên đặt await cho method bất đồng bộ — lỗi không nổi lên kịp thời, chương trình hành xử không dự đoán được.

Không bọc các gọi bất đồng bộ trong try-catch — ứng dụng sập ngay khi gặp lỗi đầu tiên.

Chỉ bắt Exception chung, bỏ qua các ngoại lệ đặc thù như UnauthorizedAccessException hoặc OperationCanceledException — dẫn tới chẩn đoán kém.

Dùng các task «fire and forget» mà không log lỗi rõ ràng — ngoại lệ bị mất trong Task.

Cho rằng nếu task kết thúc thì là thành công. Cần chờ rõ kết quả (await) và xử lý ngoại lệ.

2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Xử lý lỗi khi đọc file
Xử lý lỗi khi đọc file
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION