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

后端 未结 8 1159
一个人的身影
一个人的身影 2020-11-28 09:36

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

public class HomeController : Controller
{
    private const string MY_URL = \"http://smthing\";
    private r         


        
8条回答
  •  执笔经年
    2020-11-28 10:09

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

    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.

    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.

    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.

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

    That's because Task.Factory.StartNew does something dangerous in ASP.NET. It doesn't register the tasks execution with ASP.NET. This can lead to edge 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.

    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.

提交回复
热议问题