问题描述
我有一个非常简单的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异常:完成,而异步操作仍然悬而未决的异步模块或处理程序]
为什么会发生?我做了几个测试:
Why does it happen? I made a couple of tests:
- 如果我们去掉
任务= DownloadAsync();
从构造函数,并把它变成了首页
方法,它将没有错误的正常工作。 - 如果我们用另一种
DownloadAsync()
体返回等待Task.Factory.StartNew(()=&GT; {Thread.sleep代码(3000) ;返回给我一个错误;});
它将正常工作
- If we remove
task = DownloadAsync();
from the constructor and put it into theIndex
method it will work fine without the errors. - If we use another
DownloadAsync()
bodyreturn 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?
推荐答案
在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 它们的启动和完成。
正在发生的事情是,你射击 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();从构造,并把它 进入指数的方法,将工作良好,没有错误。
正如我上面所解释的,那是因为你不再有未处理的异步操作正在进行,而从控制器返回。
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);返回给我一个 错误;});
它将正常工作
这是因为 Task.Factory.StartNew
做了危险的ASP.NET。它不注册的任务执行与ASP.NET线程池。这可能会导致到一个池回收执行,全然不顾你的后台任务结束的情况下,导致异常中止。这就是为什么你必须使用一个机制,登记任务,如<$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 )或者<$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控制器:完成,而异步操作仍然悬而未决的异步模块或处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!