在C#中,当覆盖方法时,允许在原始方法不是时使覆盖异步。这似乎很糟糕。
令我惊讶的问题是:我被引进来协助负载测试问题。在大约500个并发用户中,登录过程将分解为重定向循环。 IIS正在使用消息“在异步操作仍处于挂起状态时完成异步模块或处理程序”来记录异常。一些搜索让我觉得有人在滥用async void
,但我在搜索源中的快速搜索一无所获。
可悲的是,我正在寻找async\s*void
(正则表达式搜索),当我应该寻找更像async\s*[^T]
的东西时(假设任务不完全合格......你明白了)。
我后来在基本控制器中发现async override void onActionExecuting
。显然,这必须是问题,而且确实如此。修复它(使其暂时同步)解决了问题。
但它给我留下了一个问题:为什么当调用代码永远不会等待它时,你可以将覆盖标记为异步?
答案 0 :(得分:27)
当基类(或接口)声明一个返回Task的虚方法时,只要返回Task,就可以覆盖它。 async
关键字只是编译器将您的方法转换为状态机的提示。虽然编译器在你的方法上做了它的黑魔法,但编译后的方法仍然会返回一个任务。
对于void
个虚拟方法,您可以覆盖一个而不使用 async
关键字(显然),并在其中启动一个未经等待的任务。当您覆盖 async
关键字并在正文中使用await
时,会发生什么情况。调用者不会等待创建的任务(因为"原始"签名是void
)。两种情况都类似*:
public override void MyVirtualMethod()
{
// Will create a non awaited Task (explicitly)
Task.Factory.StartNew(()=> SomeTaskMethod());
}
public override async void MyVirtualMethod()
{
// Will create a non awaited Task (the caller cannot await void)
await SomeTaskMethod();
}
Stephen Cleary's article对此有一些注意事项:
* SomeTaskMethod
的实现,底层框架,SynchronizationContext和其他因素可能会导致上述每种方法产生不同的结果。
答案 1 :(得分:11)
您可以覆盖async
方法,因为async不是方法签名的一部分。实际上,异步允许在您的方法中使用await
关键字通过在其中创建状态机。
您可以在此处找到有关异步的更多信息:http://blog.sublogic.com/2012/05/14/async-isnt-really-part-of-your-method-signature/
答案 2 :(得分:6)
async
不属于“合同”。这是一个实现细节,在我看来,不幸的是,它出现在错误的地方。
更改一个(非async
)方法,将Task
返回到async
一个(反之亦然)而不是一个突破性变更,并且不需要,这是完全合法的。呼叫者重新编译。
进一步表明它不属于合同的一部分是你不允许在界面中将函数标记为async
,并且在这里,完全可以用非{{async
覆盖async
1}}或反之亦然。