使用Async Await返回视图

5

我希望能够在后台运行一个进程。这个进程是通过单击操作链接来执行的。

调用该操作的动作:

   public async Task<ActionResult> ProcessRec()
   {
        await Task.Run(() => waitTimer());
        return RedirectToAction("Index", "Home");
   }

   public void waitTimer()
   {
        Thread.Sleep(10000);
   }

然而,它会在完整的10秒钟之后才将我重定向到我的“Index,Home”操作。 我对Await/Async非常陌生,所以我知道我在这里解释错误了。 我该如何让应用程序在等待计时器在后台执行的同时返回此操作? 谢谢!


你使用的是哪个版本的.NET?.NET 4.5.2引入了一个功能,使得正确地执行这个操作更容易。 - Scott Chamberlain
我已经编辑了你的标题。请参考“问题的标题应该包含“标签”吗?”,在那里达成共识是“不应该”。 - John Saunders
4个回答

5
await,正如您所发现的那样,在完成之前会阻止响应返回给用户。通常情况下,您可以将后台工作放在另一个线程上,并通过不等待来“点火并忘记”,但是在ASP.NET中,IIS将关闭未使用的AppDomains,而Task.Run不会告诉IIS您的后台线程“正在使用AppDomain”,因此在AppDomain关闭期间,您的后台线程可能会被终止并出现Thread.Abort()
如果您使用的是.NET 4.5.2或更新版本,则可以通过QueueBackgroundWorkItem告诉IIS您有一个需要保持活动状态的后台工作者。您可以像这样使用它。
   public ActionResult ProcessRec()
   {
        HostingEnvironment.QueueBackgroundWorkItem(waitTimer);
        return RedirectToAction("Index", "Home");
   }

   public void waitTimer(CancellationToken token)
   {
        Thread.Sleep(10000);
   }
   //You also could do
   public async Task waitTimer2(CancellationToken token)
   {
        await Task.Delay(10000);
   }

现在这并不能保证IIS不会关闭您的应用程序域,但它确实让它知道您正在进行某些操作,并在尝试关闭它时请求更多时间(默认情况下,在关闭开始后您有多达90秒的额外时间来完成所有排队的后台项目)。
如需更多信息,请阅读介绍此MSDN博客

4.5版本中没有HostingEnvironment.QueueBackgroundWorkItem。 - rnofenko
我已更新到4.5.2版本,上述内容完美地满足了我的需求!非常感谢! - Norman Bentley

2
这段话的意思是:当使用了await关键字时,它会异步等待操作完成。在操作完成之前,它会将线程返回到线程池中。因此,等待10秒钟后才会重定向到“Index, Home”操作。
对于第二个问题,Task.Run存在风险,因为它不会将工作注册到IIS中,可能会导致问题。可以使用BackgroundTaskManager或HangFire,它们会将执行注册到ASP.NET中。
BackgroundTaskManager.Run(() => { waitTimer() };
return RedirectToAction("Index", "Home");

如果您无法升级到4.5.2版本使用QueueBackgroundWorkItem,那么这两个链接库是非常好的替代品。 - Scott Chamberlain
@Scott 是的,我看到他说了.NET 4.5 :) - Yuval Itzchakov

1
我在考虑发送消息到队列(如Azure存储/服务总线队列),这样您就可以立即获得响应。
然后创建另一个服务来出列并处理消息(执行长时间运行的任务)。
此外,如果这是Azure网站(Web应用程序),您可以使用Web作业!
希望这有所帮助。

-1
public async Task<IActionResult> Index()
    {
        return await Task.Run(() =>
        {
            return View();
        });
    }

3
请尝试解释为什么这样做有效。这有助于人们理解正在发生的事情。 - Hoppo
1
另外,既然您已经使用了匿名函数,您可以删除 { 大括号 }return。只需使用例如 Index() => await Task.Run(() => View()); 即可。 - Jeremy Caney

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接