异步太虚,ASP.Net,并计数未完成的操作的 [英] Async Void, ASP.Net, and Count of Outstanding Operations

查看:226
本文介绍了异步太虚,ASP.Net,并计数未完成的操作的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图理解为什么在ASP.Net应用程序异步无效方法可能导致以下异常,而似乎异步任务不会:

I am trying to understand why an async void method in an ASP.Net application can result in the following exception, while it appears that async Task will not:

System.InvalidOperationException: An asynchronous module or handler 
completed while an asynchronous operation was still pending

我是比较新的.NET中异步的世界,但不要觉得我已经试图通过一些现有的资源下运行这一块,包括所有的以下内容:

I am relatively new to the world of async in .NET, but do feel like I've tried to run this one down via a number of existing resources, including all of the following:

  • What's the difference between returning void and returning a Task?
  • It's All About the SynchronizationContext
  • Async Syntactic Sugar Suggestions
  • Async in ASP.NET

从这些资源,据我所知,最好的做法是典型的返回任务,并避免异步无效。我也明白,完成时异步无效递增未完成的操作的计数时,该方法被调用,它递减。这听起来像是回答我的问题中的至少一部分。然而,我所缺少的是,当我返回任务发生了什么以及为什么这样做使事情的工作。

From these resources, I understand the best practice is to typically return Task and avoid async void. I also understand that async void increments the count of outstanding operations when the method is called and decrements it when it is completed. This sounds like at least part of the answer to my question. However, what I am missing is what happens when I return Task and why doing so makes things "work".

下面是一个人为的例子来进一步说明我的问题:

Here is a contrived example to further illustrate my question:

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;
    }
}

在一个相关的说明,如果我的异步无效的理解是正确的,那么是不是一种错误的认为异步作废的是射后不理,在这种情况下,因为ASP.Net实际上没有忘记呢?

On a related note, if my understanding of async void is correct, then isn't it kind of wrong to think of async void as "fire and forget" in this scenario since ASP.Net is not actually forgetting about it?

推荐答案

微软将异步到ASP时所做的决定,以避免尽可能多的向后兼容性问题成为可能。净。他们想将它推向所有的一ASP.NET的 - 所以异步为的WinForms,MVC,的WebAPI,SignalR等支持

Microsoft made the decision to avoid as much backwards-compatibility issues as possible when bringing async into ASP.NET. And they wanted to bring it to all of their "one ASP.NET" - so async support for WinForms, MVC, WebAPI, SignalR, etc.

从历史上看,ASP.NET一直支持清洁异步操作,因为通过基于事件的异步模式(EAP),其中异步组件通知的的SynchronizationContext .NET 2.0的启动和完成。 .NET 4.5带来的这种支持的第一个还算丰厚的变化,更新了核心ASP.NET异步类型,以更好地使基于任务的异步模式(TAP,即异步

Historically, ASP.NET has supported clean asynchronous operations since .NET 2.0 via the Event-based Asynchronous Pattern (EAP), in which asynchronous components notify the SynchronizationContext of their starting and completing. .NET 4.5 brings the first fairly hefty changes to this support, updating the core ASP.NET asynchronous types to better enable the Task-based Asynchronous Pattern (TAP, i.e., async).

在此期间,每一个不同的框架(的WebForms,MVC等)都发展自己的方式与核心互动,保持的向后兼容性的一个优先事项。在试图帮助开发人员,核心ASP.NET 的SynchronizationContext 与您所看到的异常增强;它会赶上很多用法错误。

In the meantime, each different framework (WebForms, MVC, etc) all developed their own way to interact with that core, keeping backwards compatibility a priority. In an attempt to assist developers, the core ASP.NET SynchronizationContext was enhanced with the exception you're seeing; it will catch many usage mistakes.

在WebForms的世界里,他们有 RegisterAsyncTask ,但很多人只是使用异步无效事件处理程序来代替。因此,ASP.NET 的SynchronizationContext 将允许异步无效在页面生命周期中适当的时候,如果你用它不适当的时候它会产生一个例外。

In the WebForms world, they have RegisterAsyncTask but a lot of people just use async void event handlers instead. So the ASP.NET SynchronizationContext will allow async void at appropriate times during the page lifecycle, and if you use it at an inappropriate time it will raise that exception.

在MVC /的​​WebAPI / SignalR世界,框架是为服务更有条理。因此,他们能够采取异步任务在一个非常自然的方式,只需要处理返回的工作 - 一个非常干净的抽象。作为一个侧面说明,你不需要 AsyncController 了; MVC知道它是异步的,只是因为它返回一个工作

In the MVC/WebAPI/SignalR world, the frameworks are more structured as services. So they were able to adopt async Task in a very natural fashion, and the framework only has to deal with the returned Task - a very clean abstraction. As a side note, you don't need AsyncController anymore; MVC knows it's asynchronous just because it returns a Task.

然而,如果你试图返回工作的使用异步无效,这不是支持。还有什么理由来支持它;这将是非常复杂的只是支持不应该用户做的反正。请记住,异步无效 ASP.NET 的SynchronizationContext 直接通知核心,完全绕过了MVC框架。 MVC框架知道如何等待你的工作,但它甚至不知道关于异步无效,所以它返回完成到ASP.NET核心,认为它不是的真正的完整。

However, if you try to return a Task and use async void, that's not supported. And there's little reason to support it; it would be quite complex just to support users that aren't supposed to be doing that anyway. Remember that async void notifies the core ASP.NET SynchronizationContext directly, bypassing the MVC framework completely. The MVC framework understands how to wait for your Task but it doesn't even know about the async void, so it returns completion to the ASP.NET core which sees that it's not actually complete.

这可能会导致问题的两种方案:

This can cause problems in two scenarios:


  1. 您正在尝试使用,使用异步无效一些图书馆或诸如此类的东西。遗憾,但明显的事实是,该库被打破,并且必须是固定的。

  2. 您正在包装一个EAP组件到工作并正确使用的await 。因为EAP组件与的SynchronizationContext 直接交互,这可能会导致问题。在这种情况下,最好的解决办法是那么它支持TAP自然修改类型或使用TAP类型更换它(例如,的HttpClient 而不是 Web客户端)。如果做不到这一点,你可以使用TAP-过APM代替TAP-过EAP的。如果没有那些是可行的,你可以使用 Task.Run 在你的TAP-过EAP包装。

  1. You're trying to use some library or whatnot that uses async void. Sorry, but the plain fact is that the library is broken, and will have to be fixed.
  2. You're wrapping an EAP component into a Task and properly using await. This can cause problems because the EAP component interacts with SynchronizationContext directly. In this case, the best solution is to modify the type so it supports TAP naturally or replace it with a TAP type (e.g., HttpClient instead of WebClient). Failing that, you can use TAP-over-APM instead of TAP-over-EAP. If neither of those are feasible, you can just use Task.Run around your TAP-over-EAP wrapper.

关于射后不理:

我个人从来不使用这个短语异步无效方法。一方面,错误处理语义肯定不适合与短语射后不理;我半开玩笑地指异步无效方法为火和崩溃。一个真正的异步射后不理的方法是,你忽略了返回<$ C $的异步任务方法C>工作,而不是等待它。

I personally never use this phrase for async void methods. For one thing, the error handling semantics most certainly do not fit in with the phrase "fire and forget"; I half-jokingly refer to async void methods as "fire and crash". A true async "fire and forget" method would be an async Task method where you ignore the returned Task rather than waiting for it.

这是说,在你ASP.NET几乎从来没有想从请求提前返回(这就是所谓的射后不理暗示)。这个答案已经太久了,但我的问题说明我的博客,一些code一起为支持ASP.NET射后不理,如果​​它是真正必要的。

That said, in ASP.NET you almost never want to return early from requests (which is what "fire and forget" implies). This answer is already too long, but I have a description of the problems on my blog, along with some code to support ASP.NET "fire and forget" if it's truly necessary.

这篇关于异步太虚,ASP.Net,并计数未完成的操作的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆