.NET(C#):多個await和和異常處理

來源:互聯網
上載者:User

第一點需要提的就是第一個await發生異常後,後續的await不會進行,但是注意此時的Task仍然會進行的,當然後面的Task如果拋出異常的話,異常則屬於未覺察異常,這個未覺察異常在.NET 4.0會在記憶體回收時引發程式崩潰,但在.NET 4.5 beta後不會引發進程崩潰。更多相關資訊可以參(.NET 4.5中關於Task的未覺察異常的更新),這裡就不多講了。

 

當第一個異常被拋出後,我們可以直接在這個await上進行try和catch來捕獲異常,就像該方法被同步執行一樣,而事實上,這個方法會被await而分割成兩半,前面的會同步執行,而await後面的代碼則會被附加在對應Task結束後執行(如果Task在此時沒有結束的話),同時當前環境的SynchronizationContext會被用來調用await後的代碼。

 

當然編譯器做了相當大的功勞,讓我們可以簡單的按照普通異常處理的方式來處理非同步呼叫的異常處理。

 

來看下面這個叫doo的async方法,方法運行開始會運行兩個Task,分別在1秒後和4秒後拋出異常,然後在後面await這兩個Task,代碼:

static async Task doo()

{

    try

    {

        Console.WriteLine("ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId);

        var t1 = Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("Task1即將拋出異常"); throw new Exception("Task1異常"); });

        var t2 = Task.Run(() => { Thread.Sleep(4000); Console.WriteLine("Task2即將拋出異常"); throw new Exception("Task2異常"); });

        await t1;

        Console.WriteLine("Task1的await執行完畢");

        await t2;

        Console.WriteLine("Task2的await執行完畢");

    }

    catch (Exception ex)

    {

        Console.WriteLine("ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId);

        Console.WriteLine("捕獲異常:" + ex.Message);

    }

}

 

 

 

接著在主方法中調用另一個async方法來await整個doo方法返回的Task,代碼如下:

static void Main(string[] args)

{

    test();

    Thread.Sleep(Timeout.Infinite);

}

 

static async void test()

{

    await doo();

    Console.WriteLine("test方法結束");

}

 

 

等待幾秒後,整個程式的完整輸出是這樣:

ManagedThreadId: 1

Task1即將拋出異常

ManagedThreadId: 3

捕獲異常:Task1異常

test方法結束

Task2即將拋出異常

 

可以看到,當Task1拋出異常後,異常被捕獲,Task2不會被await,當然Task2仍然在運行。

 

從代碼上看,try-catch在同一個方法內,給人造成是同步執行的假象,事實上,通過輸出當前線程的ManagedThreadId我們可以看到:執行try和catch代碼地區的完全是兩個線程。

 

最後如果在此類Task上再調用Wait會有什麼結果?答案則是同普通Wait一樣,需要捕獲AggregateException異常,然後通過它的InnerExceptions屬性訪問包含的子異常,不過同await的結果也一樣,此時異常也只會是第一個await中Task的異常,後續Task異常值會被默默吞掉(如果這些Task外部沒有引用的話),同時Wait方法不會等待其他沒有await的Task的執行。

 

範例程式碼:

首先把doo中的try-catch去掉。

然後主方法:

static void Main(string[] args)

{

    try

    {

        doo().Wait();

    }

    catch (AggregateException ae)

    {

        foreach(var exp in ae.InnerExceptions)

            Console.WriteLine(exp.Message);

        Console.WriteLine("結束catch");

    }

 

    Thread.Sleep(Timeout.Infinite);

}

 

結果:

Task1即將拋出異常

Task1異常

結束catch

Task2即將拋出異常

 

當catch結束後,另一個Task的異常才被拋出,Wait沒有等它,同時AggregateException也不包含另一個Task的異常。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.