首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >TAP全局异常处理程序

TAP全局异常处理程序
EN

Stack Overflow用户
提问于 2014-03-13 04:37:07
回答 5查看 4.6K关注 0票数 21

此代码引发异常。是否有可能定义一个应用程序全局处理程序来捕获它?

代码语言:javascript
复制
string x = await DoSomethingAsync();

使用.net 4.5 / WPF

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2014-03-14 02:41:44

如果我正确理解的话,这其实是个好问题。我一开始投票决定结束它,但现在撤回了我的投票。

了解在async Task方法中抛出的异常是如何在它之外传播的,这一点很重要。最重要的是,处理任务完成的代码需要观察这种异常。

例如,这里有一个简单的WPF应用程序,我在Net4.5.1上:

代码语言:javascript
复制
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_UnobservedTaskExceptionCurrentDomain_UnhandledException都不会被调用。在等待或等待_task对象之前,异常一直处于休眠状态。在上面的示例中,只有当任务得到垃圾收集的TaskScheduler_UnobservedTaskException时,才会被调用,因此才会被调用。那么这个例外就会被吞没。

可以通过在.NET中配置ThrowUnobservedTaskExceptions来激活AppDomain.CurrentDomain.UnhandledException事件并使应用程序崩溃的旧的ThrowUnobservedTaskExceptions 4.0行为

代码语言:javascript
复制
<configuration>
    <runtime>
      <ThrowUnobservedTaskExceptions enabled="true"/>
    </runtime>
</configuration>

当启用这种方式时,当异常被垃圾收集时,AppDomain.CurrentDomain.UnhandledException仍然会在TaskScheduler.UnobservedTaskException之后被触发,而不是在抛出的地方。

Stephen在他的“.NET 4.5中的任务异常处理”博客文章中描述了这种行为。关于任务垃圾回收的部分在文章的注释中进行了描述。

async Task方法就是这种情况。对于通常用于事件处理程序的async void方法,情况则完全不同。让我们这样修改代码:

代码语言:javascript
复制
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

票数 22
EN

Stack Overflow用户

发布于 2014-03-13 06:59:44

编辑了作为per @per的评论

在.NET 4.5中,在async代码中,您可以通过为TaskScheduler.UnobservedTaskException事件注册一个处理程序来处理未观察到的异常。如果您不访问Task.ResultTask.Exception属性,并且不调用Task.Wait,则异常被视为未观察到。

在未观察到的异常到达TaskScheduler.UnobservedTaskException事件处理程序后,默认行为是吞下此异常,这样程序就不会崩溃。通过添加以下内容,可以在配置文件中更改此行为:

代码语言:javascript
复制
<configuration> 
   <runtime> 
      <ThrowUnobservedTaskExceptions enabled="true"/> 
   </runtime> 
</configuration>
票数 1
EN

Stack Overflow用户

发布于 2014-05-09 11:40:56

将事件绑定到AppDomain.CurrentDomain.FirstChanceException将确保您的异常将被捕获。正如@you指出的那样,应用程序中的每个异常都会通知您,即使异常是在catch块中优雅地处理,并且应用程序继续进行。

但是,我仍然认为这个事件对于捕获应用程序停止之前抛出的最后几个异常或其他一些调试场景是有用的。

如果你想保护自己

代码语言:javascript
复制
string x = await DoSomethingAsync();

我给你的建议是,不要这样做,添加一个尝试捕捉块:-)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22369179

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档