ASP.NET控制器:完成,而异步操作仍然悬而未决的异步模块或处理程序 [英] ASP.NET Controller: An asynchronous module or handler completed while an asynchronous operation was still pending

查看:985
本文介绍了ASP.NET控制器:完成,而异步操作仍然悬而未决的异步模块或处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个非常简单的ASP.NET MVC 4控制器:

I have a very simple ASP.NET MVC 4 controller:

public class HomeController : Controller
{
    private const string MY_URL = "http://smthing";
    private readonly Task<string> task;

    public HomeController() { task = DownloadAsync(); }

    public ActionResult Index() { return View(); }

    private async Task<string> DownloadAsync()
    {
        using (WebClient myWebClient = new WebClient())
            return await myWebClient.DownloadStringTaskAsync(MY_URL)
                                    .ConfigureAwait(false);
    }
}

当我开始这个项目,我看到我的看法,他看起来不错,但是当我更新我得到以下错误页面:

When I start the project I see my view and it looks fine, but when I update the page I get the following error:

[InvalidOperationException异常:完成,而异步操作仍然悬而未决的异步模块或处理程序]

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

为什么会发生?我做了几个测试:

Why does it happen? I made a couple of tests:

  1. 如果我们去掉任务= DownloadAsync(); 从构造函数,并把它变成了首页方法,它将没有错误的正常工作。
  2. 如果我们用另一种 DownloadAsync()返回等待Task.Factory.StartNew(()=&GT; {Thread.sleep代码(3000) ;返回给我一个错误;}); 它将正常工作
  1. If we remove task = DownloadAsync(); from the constructor and put it into the Index method it will work fine without the errors.
  2. If we use another DownloadAsync() body return await Task.Factory.StartNew(() => { Thread.Sleep(3000); return "Give me an error"; }); it will work properly.

为什么不能使用 WebClient.DownloadStringTaskAsync 法的控制器的构造函数中?

Why it is impossible to use the WebClient.DownloadStringTaskAsync method inside a constructor of the controller?

推荐答案

在<一个href="http://stackoverflow.com/questions/17659603/async-void-asp-net-and-count-of-outstanding-operations">Async太虚,ASP.Net,并计数未完成的操作的中,斯蒂芬·克利里解释这个错误的根源:

In Async Void, ASP.Net, and Count of Outstanding Operations, Stephan Cleary explains the root of this error:

从历史上看,ASP.NET支持了干净的异步操作   因为通过基于事件的异步模式的.NET 2.0(EAP),的   其中异步组件通知的的SynchronizationContext   它们的启动和完成。

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.

正在发生的事情是,你射击 DownloadAsync 在你的类的构造函数,在里面你等待的异步HTTP调用。这将注册异步操作与ASP.NET 的SynchronizationContext 。当你的的HomeController 返回时,它会看到它有它尚未完成未处理的异步操作,这就是为什么它会引发一个例外。

What is happening is that you're firing DownloadAsync inside your class constructor, where inside you await on the async http call. This registers the asynchronous operation with the ASP.NET SynchronizationContext. When your HomeController returns, it sees that it has a pending asynchronous operation which has yet to complete, and that is why it raises an exception.

如果我们去掉任务= DownloadAsync();从构造,并把它   进入指数的方法,将工作良好,没有错误。

If we remove task = DownloadAsync(); from the constructor and put it into the Index method it will work fine without the errors.

正如我上面所解释的,那是因为你不再有未处理的异步操作正在进行,而从控制器返回。

As i explained above, that's because you no longer have a pending asynchronous operation going on while returning from the controller.

如果我们用另一种DownloadAsync()的身体返回等待    Task.Factory.StartNew(()=&GT; {Thread.sleep代码(3000);返回给我一个   错误;}); 它将正常工作

If we use another DownloadAsync() body return await Task.Factory.StartNew(() => { Thread.Sleep(3000); return "Give me an error"; }); it will work properly.

这是因为 Task.Factory.StartNew 做了危险的ASP.NET。它不注册的任务执行与ASP.NET线程池。这可能会导致到一个池回收执行,全然不顾你的后台任务结束的情况下,导致异常中止。这就是为什么你必须使用一个机制,登记任务,如<一个href="https://msdn.microsoft.com/en-us/library/system.web.hosting.hostingenvironment%28v=vs.110%29.aspx"><$c$c>HostingEnvironment.QueueBackgroundWorkItem.

That's because Task.Factory.StartNew does something dangerous in ASP.NET. It doesn't register the tasks execution with the ASP.NET ThreadPool. This can lead to end cases where a pool recycle executes, ignoring your background task completely, causing an abnormal abort. That is why you have to use a mechanism which registers the task, such as HostingEnvironment.QueueBackgroundWorkItem.

这就是为什么它是不可能做你在做什么,你做的方式。如果你真的想这对在后台线程,执行的发射后不管的风格,使用 HostingEnvironment (如果你在.NET 4.5.2 )或者<一href="https://github.com/StephenCleary/AspNetBackgroundTasks/blob/master/src/AspNetBackgroundTasks/BackgroundTaskManager.cs"><$c$c>BackgroundTaskManager.请注意,这样做,你正在使用一个线程池线程来执行异步IO操作,这是多余的,异步IO正是与异步等待试图克服。

That's why it isn't possible to do what you're doing, the way you're doing it. If you really want this to execute in a background thread, in a "fire-and-forget" style, use either HostingEnvironment (if you're on .NET 4.5.2) or BackgroundTaskManager. Note that by doing this, you're using a threadpool thread to do async IO operations, which is redundant and exactly what async IO with async-await attempts to overcome.

这篇关于ASP.NET控制器:完成,而异步操作仍然悬而未决的异步模块或处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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