1. 程式人生 > >C#多執行緒程式設計筆記(4.4)-處理Task任務中的異常

C#多執行緒程式設計筆記(4.4)-處理Task任務中的異常

近來在學習Eugene Agafonov編寫的《C#多執行緒程式設計實戰》(譯),做些筆記也順便分享一下^-^ 

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

namespace 處理任務中的異常
{
    class Program
    {
        static void Main(string[] args)
        {
            Task<int> task;
            try
            {
                task = Task.Run(() => TaskMethod("Task 1", 2));
                int result = task.Result;
                Console.WriteLine("Result: {0}", result);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception caught: {0}", ex);
            }
            Console.WriteLine("----------------------------------------");
            Console.WriteLine();

            try
            {
                task = Task.Run(() => TaskMethod("Task 2", 2));
                int result = task.GetAwaiter().GetResult();
                Console.WriteLine("Result: {0}", result);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception caught: {0}", ex);
            }
            Console.WriteLine("----------------------------------------");
            Console.WriteLine();

            var t1 = new Task<int>(() => TaskMethod("Task 3", 3));
            var t2 = new Task<int>(() => TaskMethod("Task 4", 2));
            var complexTask = Task.WhenAll(t1, t2);
            var exceptionHandler = complexTask.ContinueWith(t =>
              Console.WriteLine("Exception caught: {0}", t.Exception),
                TaskContinuationOptions.OnlyOnFaulted);
            t1.Start();
            t2.Start();

            Console.ReadKey();
        }

        static int TaskMethod(string name,int seconds)
        {
            Console.WriteLine("Task {0} is running on a thread id {1}.Is thread pool thread: {2}",
                name, Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsThreadPoolThread);
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            throw new Exception("Boom!");
            return 42 * seconds;
        }
    }
}

當程式啟動時,建立了一個任務並嘗試同步獲取任務結果。Result屬性的Get部分會使當前執行緒等待直到該任務完成,並將異常傳播給當前執行緒。在這種情況下,通過catch程式碼塊可以很容易地捕獲異常,但是該異常是一個被封裝的異常,叫做AggregateException。在本例中,它裡面包含一個異常,因為只有一個任務丟擲了異常。可以訪問InnerException屬性來得到底層異常。

第二個例子與第一個非常相似,不同之處是使用GetAwaiter和GetResult方法來訪問任務結果。這種情況下,無需封裝異常,因為TPL基礎設施會提前該異常。如果只有一個底層任務,那麼一次只能獲取一個原始異常,這種設計非常合適。

最後一個例子展示了兩個任務丟擲異常的情形。現在使用後續操作來處理異常。只有之前的任務完成前有異常時,該後續操作才會被執行。通過給後續操作傳遞TaskContinuationOptions.OnlyOnFaulted選項可以實現該行為。結果打印出了AggregateException,其內部封裝了兩個任務丟擲的異常。