目标:
我在我的 .Net Core 库中的异常处理行为感到困惑。这个问题的目标是理解 为什么 出现我所看到的情况。
执行摘要:
我曾认为当调用一个async
方法时,其中的代码会同步执行,直到第一个await被执行。如果是这样,那么如果在“同步代码”期间抛出异常,为什么不像正常的同步方法一样将其传播到调用方法?
示例代码:
给定以下代码在 .Net Core 控制台应用程序中:
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
try
{
NonAwaitedMethod();
}
catch (Exception e)
{
Console.WriteLine("Exception Caught");
}
Console.ReadKey();
}
public static async Task NonAwaitedMethod()
{
Task startupDone = new Task(() => { });
var runTask = DoStuff(() =>
{
startupDone.Start();
});
var didStartup = startupDone.Wait(1000);
if (!didStartup)
{
throw new ApplicationException("Fail One");
}
await runTask;
}
public static async Task DoStuff(Action action)
{
// Simulate starting up blocking
var blocking = 100000;
await Task.Delay(500 + blocking);
action();
// Do the rest of the stuff...
await Task.Delay(3000);
}
}
场景:
如果按原样运行此代码,它将抛出异常,但除非您在断点上,否则您不会知道。Visual Studio调试器或控制台不会给出任何指示,除了在输出屏幕中的一行注释。
将
NonAwaitedMethod
的返回类型从Task
更改为void
。这将导致Visual Studio调试器现在在异常处中断。它也将打印在控制台中。但值得注意的是,异常在Main
中找到的catch
语句中没有被捕获。保持
NonAwaitedMethod
的返回类型为void
,但去掉async
。还将最后一行从await runTask;
更改为runTask.Wait();
(这实质上移除了任何异步内容)。运行时,异常在Main
方法的catch
语句中被捕获。
因此,总结一下:
| Scenario | Caught By Debugger | Caught by Catch |
|------------|--------------------|-----------------|
| async Task | No | No |
| async void | Yes | No |
| void | N/A | Yes |
问题:
我认为因为在执行await
之前抛出了异常,所以它将同步执行,并通过抛出异常。
因此,我的问题是:为什么场景1或场景2都没有被catch
语句捕获?
另外,为什么从Task
切换到void
返回类型会导致调试器捕获异常?(即使我没使用那个返回类型。)
async
时,编译器会将您的方法包装成一个特殊类型,并将异常保留给您,直到您调用await
,此时异常会向上传播。 - weichchAsync
。因此,NonAwaitedMethod
方法应该被命名为NonAwaitedMethodAsync
,而DoStuff
应该被命名为DoStuffAsync
。这不适用于async void
方法。它仅适用于返回可等待类型(如Task
)的异步方法。 - Theodor Zoulias