Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Bắt exception từ async void method trong C#

async void là "fire-and-forget": không trả về Task, không thể await, exception không bị capture vào Task. Exception sẽ ném lên SynchronizationContext (UI thread) hoặc ThreadPool -> crash ứng dụng nếu không xử lý.

Ba kiểu trả về async

Return TypeException HandlingDùng khi
TaskWrap trong Task, bắt bằng try/catch awaitAsync method không trả giá trị (khuyến nghị thay void)
Task<T>Wrap trong Task, bắt bằng try/catch awaitAsync method trả giá trị
voidKhông capture, ném lên SyncContextEvent handler, top-level entry (Main)

Ví dụ: Bắt exception từ async Task (Đúng)

static async Task Main()
{
    try
    {
        await DoSomethingAsync();
    }
    catch (ArgumentNullException ex)
    {
        Console.WriteLine($"Caught: {ex.Message}");
    }
}

static async Task DoSomethingAsync()
{
    await FooAsync();
}

static async Task FooAsync()
{
    throw new ArgumentNullException("demo");
}

Vấn đề với async void


// KHÔNG THỂ BẮT EXCEPTION TỪ NGOÀI
static async void BadAsyncVoid()
{
    throw new InvalidOperationException("Boom!");
}

static void Main()
{
    try
    {
        BadAsyncVoid(); // Không await được
        Console.WriteLine("After call"); // Chạy tiếp, exception ném sau
    }
    catch (Exception ex)
    {
        Console.WriteLine("Never caught!"); // Không bao giờ chạy
    }
    // Ứng dụng crash sau vài giây (unobserved exception)
}

Giải pháp cho Event Handler (bắt buộc async void)

// WinForms / WPF / Blazor event handler
private async void Button_Click(object sender, EventArgs e)
{
    try
    {
        await RiskyOperationAsync();
    }
    catch (Exception ex)
    {
        // Xử lý tại chỗ: log, show message, v.v.
        MessageBox.Show($"Error: {ex.Message}");
        // Hoặc log to ILogger
    }
}

Global Handler cho Unobserved Task Exceptions

// App startup (Program.cs, App.xaml.cs)
TaskScheduler.UnobservedTaskException += (sender, args) =>
{
    args.SetObserved(); // Ngăn crash process
    Log.Error(args.Exception, "Unobserved task exception");
};

Best Practice

  1. Tránh async void trừ event handler.
  2. Luôn trả về Task hoặc Task<T>.
  3. Trong event handler: try/catch nội bộ, log, UI feedback.
  4. Đăng ký TaskScheduler.UnobservedTaskException làm safety net.