Jay*_*cee 6 c# winforms async-await
我在测试应用程序中应用了这些处理程序:
Application.ThreadException += Application_ThreadException;
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
Run Code Online (Sandbox Code Playgroud)
然后我在窗体上的嵌套Task/Async await上创建一个异常:
尽管处理程序被解雇 - 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");
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:
看起来我忘了用Task声明每个异步方法而不是void,因此异常处理现在可以预测.我唯一还不知道的是为什么 - 如果我在按钮事件中停止处理异常 - 当捕获异常时Application_ThreadException触发而不是TaskScheduler_UnobservedTaskException
您的原始代码执行了一些您不应该做的事情:调用async void方法.有问题的异常处理是其中一个原因.
让我们逐个看一下事件处理程序:
Application.ThreadException仅处理Winforms UI线程上的异常.异常从未进入UI线程,因此此事件不会触发.TaskScheduler.UnobservedTaskException只处理Task未被观察的s中的异常(即使这样,它们在最终确定时会这样做,如果你没有分配很多内存,这可能需要很长时间).但是未观察到的异常并没有与任何异常相关联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() Task.这反过来await来自async void button1_ClickUI线程上的处理程序.
这意味着没有未观察到的Task异常,并且在UI同步上下文中重新抛出异常.在Winforms中,这意味着Application.ThreadException被提升,这正是你所观察到的.