.NET(C#) TPL:Task中未覺察異常和TaskScheduler.UnobservedTaskException事件

來源:互聯網
上載者:User

當你在一個Task執行中拋出異常,比如:

Task.Factory.StartNew(() =>

{

    throw new Exception();

});

運行該方法,沒有任何異常拋出。

 

事實上此時Task的異常處於未覺察狀態,這個未覺察狀態的異常會在記憶體回收時終結器執行線程中被拋出。

為了誘發這個異常,我們可以通過GC.Collect來強制記憶體回收從而引發終結器處理線程,此時Task的未覺察異常會被拋出。

//在Task中拋出異常

Task.Factory.StartNew(() =>

{

    throw new Exception();

});

 

//確保任務完成

Thread.Sleep(100);

//強制垃圾會受到

GC.Collect();

//等待終結器處理

GC.WaitForPendingFinalizers();

 

OK,異常拋出,程式崩潰,如下輸出:

Unhandled Exception: System.AggregateException: A Task's exception(s) were not

bserved either by Waiting on the Task or accessing its Exception property. As a

result, the unobserved exception was rethrown by the finalizer thread. ---> Sys

em.Exception: Exception of type 'System.Exception' was thrown.

   at Mgen.Program.<Main>b__0() in E:\Users\Mgen\Documents\Visual Studio 2010\P

ojects\Mgen\Mgen\Program.cs:line 19

   at System.Threading.Tasks.Task.InnerInvoke()

   at System.Threading.Tasks.Task.Execute()

   --- End of inner exception stack trace ---

   at System.Threading.Tasks.TaskExceptionHolder.Finalize()

 

我們可以通過調用Task.Wait/WaitAll,或者引用Task<T>.Result屬性,或者最簡單的引用Task.Exception屬性來使Task的異常被覺察。比如這樣:

 

通過Task.Wait手動捕獲AggregateException:

try

{

    Task.WaitAll(

        Task.Factory.StartNew(() =>

        {

            throw new Exception();

        }));

}

catch (AggregateException)

{ }

 

//確保任務完成

Thread.Sleep(100);

//強制垃圾會受到

GC.Collect();

//等待終結器處理

GC.WaitForPendingFinalizers();

這樣就不會有任何異常拋出(即使是終結器線程已經結束)。

 

當然最簡單的就是直接引用一下Task.Exception屬性:

注意這裡使用Task.ContinueWith是為了避免直接引用Task變數,這樣記憶體回收可以處理這個Task對象!

//使用Task.ContinueWith可以避免直接引用Task變數,這樣記憶體回收可以處理這個Task對象!

Task.Factory.StartNew(() =>

{

    throw new Exception();

}).ContinueWith(t => { var exp = t.Exception; });

 

//確保任務完成

Thread.Sleep(100);

//強制垃圾會受到

GC.Collect();

//等待終結器處理

GC.WaitForPendingFinalizers();

同樣不會有異常拋出。

 

另外,可以通過TaskContinuationOptions.OnlyOnFaulted來使引用Exception屬性只發生在發生異常時(即Exception為null的時候沒必要再去引用它),代碼:

Task.Factory.StartNew(() =>

{

    throw new Exception();

}).ContinueWith(t => { var exp = t.Exception; }, TaskContinuationOptions.OnlyOnFaulted);

 

最後是TaskScheduler.UnobservedTaskException事件,該事件是所有未覺察異常被拋出前的最後可以將其覺察的方法。通過UnobservedTaskExceptionEventArgs.SetObserved方法來將異常標記為已覺察。

 

代碼:

TaskScheduler.UnobservedTaskException += (s, e) =>

{

    //設定所有未覺察異常被覺察

    e.SetObserved();

};

 

Task.Factory.StartNew(() =>

    {

        throw new Exception();

    });

 

//確保任務完成

Thread.Sleep(100);

//強制垃圾會受到

GC.Collect();

//等待終結器處理

GC.WaitForPendingFinalizers();

OK,沒有異常拋出。

 

 

注意在.NET 4.5後,這個現象已改變,請參考:

.NET 4.5(C#):關於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.