我在测试应用程序中应用了这些处理程序:
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
方法。 Task
被 Task.Run()
解包,然后被 await
处理,所以异常被转移到 Hello()
任务
。这反过来又是从 UI 线程上的 async void button1_Click
处理程序await
ed。
这一切都意味着没有未观察到的 Task
异常,并且在 UI 同步上下文中重新抛出该异常。在 Winforms 中,这意味着引发了 Application.ThreadException
,这正是您所观察到的。
关于c# - 未处理的异常处理程序后 Winforms 应用程序仍然崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20542212/