c# - ASP.NET Controller : An asynchronous module or handler completed while an asynchronous operation was still pending

标签 c# .net asp.net-mvc-4 async-await webclient

我有一个非常简单的 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);
    }
}

当我启动项目时,我看到了我的 View ,它看起来不错,但是当我更新页面时,我收到以下错误:

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

为什么会这样?我做了几个测试:

  1. 如果我们从构造函数中删除 task = DownloadAsync(); 并将其放入 Index 方法中,它将正常工作而不会出现错误。
  2. 如果我们使用另一个 DownloadAsync() 主体 return await Task.Factory.StartNew(() => { Thread.Sleep(3000); return "Give me an error"; } ); 它将正常工作。

为什么不能在 Controller 的构造函数中使用 WebClient.DownloadStringTaskAsync 方法?

最佳答案

Async Void, ASP.Net, and Count of Outstanding Operations , Stephan Cleary 解释了这个错误的根源:

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 调用中您在 await 中。这会向 ASP.NET SynchronizationContext 注册异步操作。当您的 HomeController 返回时,它发现它有一个尚未完成的挂起异步操作,这就是它引发异常的原因。

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

正如我上面所解释的,那是因为从 Controller 返回时,您不再有挂起的异步操作在进行。

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 注册任务执行。这可能会导致执行池回收的边缘情况,完全忽略您的后台任务,从而导致异常中止。这就是为什么你必须使用一种机制来注册任务,例如 HostingEnvironment.QueueBackgroundWorkItem .

这就是为什么不可能按照您的方式做您正在做的事情。如果您真的希望它在后台线程中以“即发即弃”的方式执行,请使用 HostingEnvironment(如果您使用的是 .NET 4.5.2)或 BackgroundTaskManager .请注意,通过执行此操作,您将使用线程池线程来执行异步 IO 操作,这是多余的,并且正是使用 async-await 的异步 IO 试图克服的问题。

关于c# - ASP.NET Controller : An asynchronous module or handler completed while an asynchronous operation was still pending,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28805796/

相关文章:

c# - 在 MVC4 Twitter Bootstrap C# 中设置文本框格式

javascript - Asp.Net4 Javascript/jquery 缩小错误

c# - 将 Func 转换为委托(delegate)

c# - 找不到参数引用 (%1) 的描述字符串

c# - Wpf 绝对与相对包 URI

c# - 是否有与 .net 的纯基于文件的 MSSQL 数据库等效的 PHP/Java?

c# - MVC 异步 Controller Action

c# - Java 有运算符 >>> 和 <<< 这有点不同 - 请帮助

c# - Base Controller ASP.NET MVC 3 中的这个 Custom Principal 是不是非常低效?

C# 正则表达式通过 url 从 youtube 和 vimeo 获取视频 ID