在只返回Task的异步方法上使用await
c#
在我的 ASP.NET Core 应用程序中,我以以下形式创建了几个(十几个)方法
public async Task DoStuff()
{
// ...
}
所以,没有返回类型。
后来,我意识到我不小心忘记在调用方方法中到处包含此类方法(显然没有 async 关键字,因为异步“僵尸病毒”还没有传播那么远)。
在执行过程中,没有任何不良后果。
问题是,当这种事情发生时,Visual Studio 也不会生成任何警告消息,我问自己,在这种情况下,实际上是否存在忽略等待的危险?我知道 await 应该自然地应用于每个异步方法,但是当调用者实际上没有可使用的返回值时,我真的不明白这背后的原因。也许有捕获异常的东西?
我还没有找到任何明确的答案,因为一般声明是“只包含等待”。不管你信不信,这个异步/等待的东西,我相对较新,不时地反复咬我。
回答
在执行过程中,没有任何不良后果。
我不同意。生成的代码是危险的。ASP.NET pre-Core 能够检测到类似的情况并抛出异常(“异步模块或处理程序已完成,而异步操作仍处于挂起状态”)。由于技术原因,ASP.NET Core 无法检测到这种情况,因此您不会得到“安全网”异常,但情况本身仍然同样糟糕。
问题是,当这种事情发生时,Visual Studio 也不会生成警告消息
你没有得到CS4014(“因为没有等待这个调用,当前方法的执行会在调用完成之前继续。考虑将 await 运算符应用于调用的结果。”)?
在这种情况下,实际上是否存在忽略等待的危险?我知道 await 应该自然地应用于每个异步方法,但是当调用者实际上没有可使用的返回值时,我真的不明白这背后的原因。也许有捕获异常的东西?
是的,有危险。 Task(即使没有结果类型)用于两件事:调用者知道操作何时完成,调用者检测该操作的异常。
因此,一个问题是异常被默默吞下。更具体地说,该async方法的异常被async状态机捕获并放置在返回的 上Task,然后将其忽略。
如果我自己处理上述方法中的异常(未正确等待的异常),那么我们可以说一切都很好吗?
不,因为另一个问题仍然存在:调用者不知道异步操作何时完成。这在 ASP.NET 中尤为重要,因为在操作完成之前不应发送结果。任何一种“发射后不管”的ASP.NET代码生活之外的请求/响应生命周期; 即,它是请求外部代码。
我在我的博客上详细介绍了为什么请求外部代码是危险的。总之,您的 ASP.NET 处理程序可能会过早完成,在这种情况下,请求外部代码可能会“丢失”。至少,在发送响应时,它所做的一切都不会完成;和定时关机(例如,滚动升级)的情况下,它可能无法完成在所有。