c# - TAP 全局异常处理程序

标签 c# .net exception task-parallel-library async-await

此代码抛出异常。是否可以定义一个应用程序全局处理程序来捕获它?

string x = await DoSomethingAsync();

使用 .net 4.5/WPF

最佳答案

如果我理解正确的话,这实际上是一个问题。我最初投票决定关闭它,但现在撤回了我的投票。

了解在 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_UnobservedTaskExceptionCurrentDomain_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

关于c# - TAP 全局异常处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22369179/

相关文章:

c# - WPF 应用程序中的复合 ViewModel

c# - Math.Pow 给出 "Cannot implicitly convert type ' double' to 'float' "error

c# - 以编程方式在 Listview 中选择项目,该项目绑定(bind)到数据表并且默认按排序顺序排列?

.net - 在 visual studio 中运行 sosex

c# - datagridview 行点击

c# - 编译后更改应用程序名称、图标和程序集信息

c# - 是否可以捕获或隐藏非托管异常窗口?

c# - WPF Helix如何在代码中使用 "zoom extent"viewport3d?

c# - 在哪里捕获异步 WCF 调用的 EndpointNotFoundException?

java - 在 doThrow JUNIT 之后捕获异常