C#多線程中的異常處理

常規Thread中處理異常

使用Thread建立的子線程,須要在委託中捕捉,沒法在上下文線程中捕捉異步

static void Main(string[] args)
{
    ThreadStart threadStart = DoWork;
    Thread thread = new Thread(threadStart);
    thread.Start();
    thread.Join();
}
static void DoWork()
{
    try
    {
        throw new Exception("子線程出現異常了");
    }
    catch (Exception ex)
    {
        Trace.Assert(false, "Catch In Delegate");
    }
}

Task中處理異常

1.仍然能夠在委託中捕獲異常spa

2.能夠捕獲Task.Wait() 或者 Task.Result 的 AggregateException 異常線程

try
{
    task.Wait();
}
catch (AggregateException ex)
{
    Console.WriteLine($"Error: {ex.GetType().Name}");
    foreach (Exception item in ex.InnerExceptions)
    {
        Console.WriteLine($"{item.GetType().Name}, {item.Message}");
    }
}

 AggregateException 是並行任務中捕獲的一組異常code

經過延續任務捕獲前驅任務中的異常

static void Main(string[] args)
{
    Task task = Task.Run(() => throw new Exception("前驅任務異常了"));
    Task faultedTask = task.ContinueWith(antecedentTask =>
    {
        antecedentTask.Exception.Handle(eachE =>
        {
            Console.WriteLine($"Error: {eachE.Message}");
            return true;
        });
    },TaskContinuationOptions.OnlyOnFaulted);
    faultedTask.Wait();
}

前驅任務:使用Run書寫的第一個任務就是前驅任務blog

延續任務:在一個任務後使用ContinueWith添加的任務就是延續任務,延續通常是一個全新的工做線程string

TaskContinuationOptions:指定延續任務時的可配置項,默認狀況下前驅任務完成後,當即執行延續任務,OnlyOnFaulted表示只有前驅任務失敗(出異常的時候)纔會執行這一個延續任務it

Task.Exception也是一個AggregateException 異常io

注意:class

1.當指定的TaskContinuationOptions與前驅任務運行結果不一致時,強制調用延續任務Wait()會引起TaskCanceledException異常thread

static void Main(string[] args)
{
    Task task = new Task(() =>
    {
        Console.WriteLine("前驅動任務執行中...");
    });
    Task faultedTask = task.ContinueWith(antecedentTask =>
    {
        Console.WriteLine("延續動任務執行中...");
    }, TaskContinuationOptions.OnlyOnFaulted);
    task.Start();
    try
    {
        faultedTask.Wait();
    }
    catch (AggregateException ex)
    {
        Console.WriteLine($"Error: {ex.GetType().Name}");
        foreach (Exception item in ex.InnerExceptions)
        {
            Console.WriteLine($"{item.GetType().Name}, {item.Message}");
        }
    }
    Console.WriteLine($"前驅任務狀態{task.Status}");
    Console.WriteLine($"延續任務狀態{faultedTask.Status}");
}

Ctrl+F5 輸出

補充:

假如在前驅任務中出現了異常,如OnlyOnFaulted所願,會執行faultedTask任務,而且在faultedTask.Wait()中不會捕捉到前驅任務的異常,具體看下面一點

2.延續任務雖然在異步任務中提供了相似if else 的ContinueWith可是在異常處理上仍是有點侷限,看一個例子

static void Main(string[] args)
{
    Task task = Task.Run(()
        =>
    throw new Exception("前驅任務異常了"));
    Task task1 = task.ContinueWith(antecedentTask =>
    {
        throw new Exception("延續任務1異常了");
    });
    Task task2 = task1.ContinueWith(antecedentTask =>
    {
        throw new Exception("延續任務2異常了");
    });
    Task task3 = task2.ContinueWith(antecedentTask =>
    {
        throw new Exception("延續任務3異常了");
    });
    try
    {
        task3.Wait();
    }
    catch (AggregateException ex)
    {
        Console.WriteLine($"Error: {ex.GetType().Name}");
        foreach (Exception item in ex.InnerExceptions)
        {
            Console.WriteLine($"{item.GetType().Name}, {item.Message}");
        }
    }
}

Ctrl+F5 輸出

其實這樣也能夠理解,task3.Wait()只會收集task3所在工做線程上的異常,遺憾的是Task.Exception.InnerExceptions是一個只讀集合,這樣一來,每一個任務的異常只能在各自委託中處理了,事實上也應該如此,可使用TaskContinuationOptions進行靈活控制

使用CancellationTokenSource取消任務

static void Main(string[] args)
{
    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    cancellationTokenSource.Token.Register(() => 
    {
        Console.WriteLine("任務取消了");
    });
    cancellationTokenSource.CancelAfter(2000);
    Task task = Task.Run(() =>
    {
        while (true && !cancellationTokenSource.IsCancellationRequested)
        {
            Console.WriteLine("任務執行中...");
            Thread.Sleep(300); 
        }
    },
    cancellationTokenSource.Token);
    task.Wait();
    Console.WriteLine($"任務的最終狀態是:{task.Status}");
}

Ctrl+F5 輸出

正常取消的任務最終狀態是 RanToCompletion ,這裏要注意的是,CancelAfter()是在這個方法調用的那一刻開始計時的(並不是以Run開始計時,好吧,很好理解,我卻疑惑了半天)

小結:

結合 TaskContinuationOptions 和 CancellationTokenSource 能夠很好處理多任務中異常,可是編寫在異步程序仍是很繁瑣的,具體的在下一個筆記中會結合C#5.0作一個比較

相關文章
相關標籤/搜索