Dav*_*eps 47 c# asp.net async-await
我试图理解为什么ASP.Net应用程序中的异步void方法可能导致以下异常,而async Task似乎不会:
System.InvalidOperationException: An asynchronous module or handler
completed while an asynchronous operation was still pending
Run Code Online (Sandbox Code Playgroud)
我对.NET中的异步世界相对较新,但我觉得我试图通过一些现有资源来运行这个,包括以下所有内容:
从这些资源中,我了解最佳做法是通常返回Task并避免异步void.我也理解async void会在调用方法时增加未完成操作的数量,并在完成时减少它.这听起来至少是我问题答案的一部分.但是,我遗漏的是当我返回Task时会发生什么,以及为什么这样做会让事情变得"有效".
这是一个人为的例子来进一步说明我的问题:
public class HomeController : AsyncController
{
// This method will work fine
public async Task<ActionResult> ThisPageWillLoad()
{
// Do not await the task since it is meant to be fire and forget
var task = this.FireAndForgetTask();
return await Task.FromResult(this.View("Index"));
}
private async Task FireAndForgetTask()
{
var task = Task.Delay(TimeSpan.FromSeconds(3));
await task;
}
// This method will throw the following exception:
// System.InvalidOperationException: An asynchronous module or
// handler completed while an asynchronous operation was still pending
public async Task<ActionResult> ThisPageWillNotLoad()
{
// Obviously can't await a void method
this.FireAndForgetVoid();
return await Task.FromResult(this.View("Index"));
}
private async void FireAndForgetVoid()
{
var task = Task.Delay(TimeSpan.FromSeconds(3));
await task;
}
}
Run Code Online (Sandbox Code Playgroud)
在相关的说明中,如果我对async void的理解是正确的,那么在这种情况下将async void视为"火与忘"并不是错误的,因为ASP.Net实际上并没有忘记它吗?
Ste*_*ary 63
Microsoft决定在引入asyncASP.NET 时避免尽可能多的向后兼容性问题.他们想把它带到他们所有的"一个ASP.NET" - 所以async支持WinForms,MVC,WebAPI,SignalR等.
从历史上看,ASP.NET自.NET 2.0以来通过基于事件的异步模式(EAP)支持干净的异步操作,其中异步组件通知SynchronizationContext它们的启动和完成..NET 4.5为这种支持带来了第一次相当大的变化,更新了核心ASP.NET异步类型,以更好地启用基于任务的异步模式(TAP,即async).
与此同时,每个不同的框架(WebForms,MVC等)都开发了自己的方式与该核心进行交互,将向后兼容性作为优先事项.为了帮助开发人员,核心ASP.NET SynchronizationContext得到了增强,除了你所看到的; 它会捕获许多使用错误.
在WebForms世界中,他们有RegisterAsyncTask很多人只使用async void事件处理程序.因此,ASP.NET SynchronizationContext将async void在页面生命周期的适当时间允许,如果您在不适当的时间使用它,它将引发该异常.
在MVC/WebAPI/SignalR世界中,框架更加结构化为服务.所以他们能够以async Task非常自然的方式采用,框架只需要处理返回Task- 一个非常干净的抽象.作为旁注,你不再需要AsyncController了; MVC知道它是异步的,因为它返回了一个Task.
但是,如果您尝试返回Task 并使用async void,则不支持.并没有理由支持它; 只支持不应该这样做的用户,这将是非常复杂的.请记住,直接async void通知核心ASP.NET SynchronizationContext,完全绕过MVC框架.MVC框架理解如何等待你,Task但它甚至不知道async void,所以它返回完成ASP.NET核心,看到它实际上并不完整.
这可能会在两种情况下导致问题:
async void.对不起,但显而易见的事实是图书馆坏了,必须修复.Task正确使用中await.这可能会导致问题,因为EAP组件SynchronizationContext直接与之交互.在这种情况下,最好的解决方案是修改类型,使其自然支持TAP或用TAP类型替换它(例如,HttpClient代替WebClient).如果做不到这一点,您可以使用TAP-over-APM而不是TAP-over-EAP.如果这些都不可行,您可以使用Task.RunTAP-over-EAP包装器.关于"火与忘记":
我个人从不使用这句话来表达async void方法.首先,错误处理语义肯定不符合"火与忘记"这个短语; 我半开玩笑地将async void方法称为"火灾和崩溃".真正的async"即发即弃"方法是一种async Task忽略返回Task而不是等待它的方法.
也就是说,在ASP.NET中,你几乎不想从请求中提前返回(这就是"火与忘记"所暗示的).这个答案已经太长了,但我对我博客上的问题进行了描述,并提供了一些支持ASP.NET的代码"如果真的有必要的话就会忘记".
| 归档时间: |
|
| 查看次数: |
21783 次 |
| 最近记录: |