c# - 未处理的异常处理程序后 Winforms 应用程序仍然崩溃

标签 c# winforms async-await

我在测试应用程序中应用了这些处理程序:

    Application.ThreadException += Application_ThreadException;
    Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
    AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
    TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;

然后我在表单上的嵌套任务/异步等待上创建异常:

尽管处理程序被解雇 - CurrentDomain.UnhandledException - 应用程序仍然崩溃。

<罢工>

为什么它会崩溃而不显示对话框并继续运行?

System.Exception 未处理 消息:mscorlib.dll 中出现类型为“System.Exception”的未处理异常 附加信息:dd

private async void button1_Click(object sender, EventArgs e)
{
    Console.WriteLine("Main " + Thread.CurrentThread.ManagedThreadId);
    try
    {
        await Hello();
    }
    catch (Exception ex)
    {
        Console.WriteLine("Exception on main  " + Thread.CurrentThread.ManagedThreadId);
    }
}

private async static Task Hello() //changed from void
{
    await Task.Run(() => new IGetRun().Run1());
}

internal class IGetRun
{
    public async Task Run1() //changed from void
    {
        Console.WriteLine("Run1 " + Thread.CurrentThread.ManagedThreadId);
            await Task.Run(() => new IGetRun2().Run2());
    }
}

internal class IGetRun2
{
    public void Run2()
    {
        Console.WriteLine("Run2 " + Thread.CurrentThread.ManagedThreadId);
        throw new Exception("dd");
    }
}

编辑: 看起来我忘了用 Task 声明每个异步方法而不是 void,因此异常处理现在可以预测地工作。唯一我仍然不知道的是为什么 - 如果我停止处理按钮事件中的异常 - 当异常被捕获时 Application_ThreadException被解雇而不是TaskScheduler_UnobservedTaskException

最佳答案

您的原始代码做了一些您永远不应该做的事情:调用 async void 方法。有问题的异常处理是造成这种情况的原因之一。

让我们一一看一下事件处理程序:

  • Application.ThreadException 仅处理 Winforms UI 线程上的异常。异常从未到达 UI 线程,因此不会触发此事件。
  • TaskScheduler.UnobservedTaskException 仅处理未被观察到的 Task 的异常(即便如此,它也会在它们完成时处理,如果您可能需要很长时间'没有分配太多内存)。但是未观察到的异常与任何 Task 无关,因此它也不会触发。
  • AppDomain.CurrentDomain.UnhandledException 处理在前两个事件处理程序之后仍被视为未观察到的所有异常。但它不能用于设置观察到的异常,因此应用程序仍然终止。这是唯一实际触发的处理程序。

为什么前两个处理程序没有触发?在您的代码中,您有 Task.Run(() => new IGetRun().Run1()),其中 Run1() 是一个 async void 抛出异常的方法。

async void 方法中出现未观察到的异常时,会在当前同步上下文中重新抛出该异常。但是由于该方法是在线程池线程上运行的(因为 Task.Run()),所以没有同步上下文。所以异常是在线程池线程上抛出的,前两个事件处理程序无法观察到。

正如您已经发现的那样,解决此问题的方法是使用 async Task 方法并正确地 await 它们。


在您更新的代码中,Run1() 现在是一个async Task 方法。 TaskTask.Run() 解包,然后被 await 处理,所以异常被转移到 Hello() 任务。这反过来又是从 UI 线程上的 async void button1_Click 处理程序awaited。

这一切都意味着没有未观察到的 Task 异常,并且在 UI 同步上下文中重新抛出该异常。在 Winforms 中,这意味着引发了 Application.ThreadException,这正是您所观察到的。

关于c# - 未处理的异常处理程序后 Winforms 应用程序仍然崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20542212/

相关文章:

c# - 根据 Linq 查询中的属性值过滤掉重复的 XElement

c# - 更新 MongoDb 中对象的所有属性

c# - 有没有办法接受使用 C# 静默安装 .msi 文件的许可协议(protocol)?

wpf - 如何将 WPF 对话框添加到 Winforms 项目

c# - 操作运行时异步取消

c# - 在 Asp Core 项目中启用 CORS 的问题

c# - 显示未聚焦的新 WinForms 窗口

javascript - 如何避免 UI5 中的 "Synchronous XMLHttpRequest on the main thread"警告?

c# - EntityFramework ToListAsync() 不起作用

c# - 处理具有多个子控件的用户控件的双击事件