回答
這實際上是一個好問題,如果我理解正確。我最初投票決定關閉它,但現在撤回了我的投票。
瞭解如何將在async Task
方法內拋出的異常傳播到其外部很重要。最重要的是,這種異常需要通過處理完成任務的代碼觀察到。
例如,下面是一個簡單的WPF應用程序,我在NET 4.5.1:
using System;
using System.Threading.Tasks;
using System.Windows;
namespace WpfApplication_22369179
{
public partial class MainWindow : Window
{
Task _task;
public MainWindow()
{
InitializeComponent();
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
_task = DoAsync();
}
async Task DoAsync()
{
await Task.Delay(1000);
MessageBox.Show("Before throwing...");
GCAsync(); // fire-and-forget the GC
throw new ApplicationException("Surprise");
}
async void GCAsync()
{
await Task.Delay(1000);
MessageBox.Show("Before GC...");
// garbage-collect the task without observing its exception
_task = null;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
}
void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
MessageBox.Show("TaskScheduler_UnobservedTaskException:" + e.Exception.Message);
}
void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
MessageBox.Show("CurrentDomain_UnhandledException:" + ((Exception)e.ExceptionObject).Message);
}
}
}
一旦ApplicationException
已經扔,這是不言而喻沒有觀察到。 TaskScheduler_UnobservedTaskException
和CurrentDomain_UnhandledException
都不會被調用。在等待或等待_task
對象之前,異常處於休眠狀態。在上面的例子中,它從來沒有被觀察到,因此TaskScheduler_UnobservedTaskException
只有在任務被垃圾回收時纔會被調用。。然後這個異常將被吞入。
老.NET 4.0的行爲,其中AppDomain.CurrentDomain.UnhandledException
事件被炒魷魚和應用程序崩潰,可以通過配置ThrowUnobservedTaskExceptions
在app.config
啓用:
<configuration>
<runtime>
<ThrowUnobservedTaskExceptions enabled="true"/>
</runtime>
</configuration>
當啓用此方式,AppDomain.CurrentDomain.UnhandledException
仍將被解僱後TaskScheduler.UnobservedTaskException
當異常得到垃圾回收,而不是它拋出的地方。
此行爲由Stephen Toub在其"Task Exception Handling in .NET 4.5"博客文章中描述。關於任務垃圾收集的部分在帖子的評論中描述。
這就是async Task
方法的情況。這個故事與async void
方法完全不同,它們通常用於事件處理程序。讓我們改變了這樣的代碼:
public MainWindow()
{
InitializeComponent();
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
this.Loaded += MainWindow_Loaded;
}
async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
await Task.Delay(1000);
MessageBox.Show("Before throwing...");
throw new ApplicationException("Surprise");
}
因爲它是async void
沒有Task
參考扶住(所以沒有什麼可以觀察到可能或垃圾收集後)。在這種情況下,立即在當前同步上下文中引發異常。對於WPF應用程序,Dispatcher.UnhandledException
將先被觸發,然後是Application.Current.DispatcherUnhandledException
,然後AppDomain.CurrentDomain.UnhandledException
。最後,如果沒有處理這些事件(EventArgs.Handled
未設置爲true
),則無論ThrowUnobservedTaskExceptions
設置如何,該應用都會崩潰。 TaskScheduler.UnobservedTaskException
是不是在這種情況下被解僱,出於同樣的原因:沒有Task
。
Noseratio你在理解我的問題上很有用。但是,在GC運行之前不會引發UnobservedTaskException,這使得它在功能上無用。 Toub在其帖子的問題部分提供了IgnoreExceptions解決方案。我不明白爲什麼編譯器不會在創建異步方法包裝器時添加它。如果事件得到處理,則在繼續內部應該引發UnobservedTaskExpception。 – Sam
@Sam,IMO這種行爲非常有道理。 「任務」對象是一個承諾,是一個延遲的結果。剛剛完成的事實並不意味着它必須馬上被觀察到。由於與其他任務組合(例如'WhenAll')或其他異步邏輯特性,可能會在10分鐘內觀察到它。爲什麼編譯器會對此做出任何假設並打破這種邏輯?它不應該。 – Noseratio
您可以完全控制這一點。可以用'try/catch'來處理異常,或者使用類似前面提到的'IgnoreException'來標記爲觀察到的異常。 – Noseratio
在async
代碼,你可以通過註冊TaskScheduler.UnobservedTaskException
事件的處理程序處理沒有觀察到異常EDITED按@ Noseration的評論
在.NET 4.5。如果您不訪問Task.Result
,Task.Exception
屬性,並且您沒有撥打Task.Wait
,則認爲例外情況未被發現。
在未觀察到異常到達TaskScheduler.UnobservedTaskException
事件處理程序後,默認行爲是吞下此異常,以免程序崩潰。這種行爲可以在配置文件中加入更改如下:
<configuration>
<runtime>
<ThrowUnobservedTaskExceptions enabled="true"/>
</runtime>
</configuration>
是的,但在這種情況下,他正在等待結果,因此該任務將被視爲已觀察到。 –
這個**對於.NET 4.0是**,但對於.NET 4.5+不再適用。查看[我的答案](http://stackoverflow.com/a/22395161/1768303)瞭解詳情。 – Noseratio
那麼,你將如何定義一個應用程序的全局處理程序來處理這種情況的一個例外?
string x = DoSomething();
機會是你的問題的答案是完全一樣的。看起來您正在等待一個異步方法,並且編譯器會竭盡全力確保異步方法中發生的任何異常都以一種可讓您像在同步代碼中處理它的方式傳播和解除。這是異步/等待的主要好處之一。
將事件綁定到AppDomain.CurrentDomain.FirstChanceException
將保證您的異常將被捕獲。正如@Noseratio指出的那樣,即使在catch塊中正常處理異常並且應用程序繼續運行,您也會收到應用程序中每個異常的通知。
但是,我仍然看到這個事件對於至少捕獲應用程序暫停之前拋出的最後一些異常或者其他一些調試場景很有用。
如果你想保護自己免受這種
string x = await DoSomethingAsync();
我給你的建議是,不這樣做,將一個try catch塊:-)
您可以隨時做以下操作使用Application.DispatcherUnhandledException
方法處理異常。當然,它會在TargetInvocationException
之內給你,可能不如其他方法漂亮。但它工作得很好
_executeTask = executeMethod(parameter);
_executeTask.ContinueWith(x =>
{
Dispatcher.CurrentDispatcher.Invoke(new Action<Task>((task) =>
{
if (task.Exception != null)
throw task.Exception.Flatten().InnerException;
}), x);
}, TaskContinuationOptions.OnlyOnFaulted);
- 1. 在全局異常處理程序中處理線程異常?
- 2. Spring MVC:全局異常處理程序
- 3. 「全局」異常處理程序
- 4. Java:全局異常處理程序
- 5. iOS - 全局異常處理程序
- 6. 全局異常處理程序WPF
- 7. BlackBerry - 全局異常處理程序
- 8. EJB全局異常處理程序
- 9. Java全局異常處理程序
- 10. 讓異常泡泡的JSF全局異常處理程序
- 11. WCF全局異常處理
- 12. Monotouch全局異常處理
- 13. WinForms全局異常處理?
- 14. Flask全局異常處理
- 15. Angular2全局異常處理
- 16. CLR存儲過程中的全局異常處理程序
- 17. RouteBuilder.onException是否創建全局異常處理程序或路由特定的異常處理程序?
- 18. 是否有NServiceBus的全局異常處理程序?
- 19. resteasy中的全局自定義異常處理程序
- 20. Eclipse RCP應用程序中的全局異常處理
- 21. 全局異常處理程序在VB 6
- 22. asp mvc5全局異常處理程序返回同一頁
- 23. 如何在DLL中創建全局異常處理程序?
- 24. 如何在postgres中做全局異常處理程序?
- 25. 在Java全局異常處理程序中的可見性
- 26. 通過類庫的全局異常處理程序 - 公開MyApplication?
- 27. 全局JavaScript異常處理程序(在Chrome中)
- 28. 爲Visual Studio 2010製作全局異常處理程序VsPackage
- 29. Task.Factory.Start的全局未捕獲異常處理程序新任務
- 30. 如何爲WCF服務創建全局異常處理程序?
try/catch圍繞該聲明出現了什麼問題? –
提示創建Applicaton.DispatcherUnhandledException的相同事物。 – Sam
那麼這是爲可能發生的異常而創建的,您無法捕捉。一般來說,您應該捕獲代碼中可能出現的異常。但是,這裏有一些鏈接可能會幫助你相處:http://stackoverflow.com/questions/14167746/appdomain-unhandledexception&http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception% 28v = vs.110%29.aspx –