此代码引发异常。是否有可能定义一个应用程序全局处理程序来捕获它?
string x = await DoSomethingAsync();使用.net 4.5 / WPF
发布于 2014-03-14 02:41:44
如果我正确理解的话,这其实是个好问题。我一开始投票决定结束它,但现在撤回了我的投票。
了解在async Task方法中抛出的异常是如何在它之外传播的,这一点很重要。最重要的是,处理任务完成的代码需要观察这种异常。
例如,这里有一个简单的WPF应用程序,我在Net4.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中配置ThrowUnobservedTaskExceptions来激活AppDomain.CurrentDomain.UnhandledException事件并使应用程序崩溃的旧的ThrowUnobservedTaskExceptions 4.0行为
<configuration>
<runtime>
<ThrowUnobservedTaskExceptions enabled="true"/>
</runtime>
</configuration>当启用这种方式时,当异常被垃圾收集时,AppDomain.CurrentDomain.UnhandledException仍然会在TaskScheduler.UnobservedTaskException之后被触发,而不是在抛出的地方。
Stephen在他的“.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。
发布于 2014-03-13 06:59:44
编辑了作为per @per的评论
在.NET 4.5中,在async代码中,您可以通过为TaskScheduler.UnobservedTaskException事件注册一个处理程序来处理未观察到的异常。如果您不访问Task.Result、Task.Exception属性,并且不调用Task.Wait,则异常被视为未观察到。
在未观察到的异常到达TaskScheduler.UnobservedTaskException事件处理程序后,默认行为是吞下此异常,这样程序就不会崩溃。通过添加以下内容,可以在配置文件中更改此行为:
<configuration>
<runtime>
<ThrowUnobservedTaskExceptions enabled="true"/>
</runtime>
</configuration>发布于 2014-05-09 11:40:56
将事件绑定到AppDomain.CurrentDomain.FirstChanceException将确保您的异常将被捕获。正如@you指出的那样,应用程序中的每个异常都会通知您,即使异常是在catch块中优雅地处理,并且应用程序继续进行。
但是,我仍然认为这个事件对于捕获应用程序停止之前抛出的最后几个异常或其他一些调试场景是有用的。
如果你想保护自己
string x = await DoSomethingAsync();我给你的建议是,不要这样做,添加一个尝试捕捉块:-)
https://stackoverflow.com/questions/22369179
复制相似问题