异步控制器(MVC),长时间运行的进程“停止”

时间:2011-11-05 12:37:37

标签: c# asp.net .net asp.net-mvc-3

我对运行一个漫长的过程很感兴趣,我想在结果开始时立即更新UI,而不是等待它完成。

我该怎么做?我已经阅读过有关异步控制器的内容,但它们似乎没有为此目的内置任何内容。

只是在结果进入时将结果保存在Application / Session对象中并使用来自客户端的轮询?我可以想到几种可能出错的方法(比如用户关闭页面,对象永远停留在Application对象中 - 需要自己管理这些对象的过期,为轮询提供额外的服务器负载等)。

有什么想法吗?

谢谢

3 个答案:

答案 0 :(得分:4)

我最近试图解决类似的事情(从长时间运行的服务器操作向客户端报告实时进度),事实证明SignalR非常适合这种情况。

基本上它是一个包装长轮询和Web套接字的库,使用(透明地)服务器和客户端上可用的任何内容。

到目前为止,我只有很好的经验。

答案 1 :(得分:1)

您可以使用ThreadPool.QueueUserWorkItem运行长时间运行的任务,它可以更新用户可以轮询的Application / Session中的状态。正如你所指出的,它有一些终身问题。除了你指出的,应用程序池可以回收 - 但也许没关系。

该操作的逻辑范围是什么?它是一个系统类型长时间运行的任务,有人/很多人需要查询进度吗?或者它是代表特定用户的长期任务?如果是后者,那么如果该用户的会话超时等就可以了......如果它是前者那么你需要更持久。例如,您可以将任务请求,状态和进度存储在数据库中。这样在应用程序重新启动它就可以在它关闭的地方拾取并且很容易被任何人查询(如果是系统级任务,则是一个决策点)。

最后一个考虑因素是您是否拥有多个Web角色(Web场/群集)。如果这是一个考虑因素而不是数据库,甚至单独的工作者角色/服务变得更合适。

所以这一切归结为它的任务类型,谁需要监控它以及耐用性要求是什么。如果它只是一个用户任务,请保持简单,queueuserworkitem和会话状态。

答案 2 :(得分:0)

这篇文章似乎描述了你想要的,简单的,没有SignalR:

ASP.NET MVC 3: Async jQuery progress indicator for long running tasks

<强>控制器:

public class HomeController:Controller    {      private static IDictionary tasks = new Dictionary();

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

 public ActionResult Start()
 {
   var taskId = Guid.NewGuid();
   tasks.Add(taskId, 0);

   Task.Factory.StartNew(() =>
   {
     for (var i = 0; i <= 100; i++)
     {
       tasks[taskId] = i; // update task progress
       Thread.Sleep(50); // simulate long running operation
     }
     tasks.Remove(taskId);
   });

   return Json(taskId);
 }

 public ActionResult Progress(Guid id)
 {
   return Json(tasks.Keys.Contains(id) ? tasks[id] : 100);
 }

}

查看:

<script type="text/javascript">

function updateMonitor(taskId, status) {
  $("#" + taskId).html("Task [" + taskId + "]: " + status);
}

$(function () {
  $("#start").click(function (e) {
   e.preventDefault();
   $.post("Home/Start", {}, function (taskId) {

     // Init monitors
     $("#monitors").append($("<p id='" + taskId + "'/>"));
     updateMonitor(taskId, "Started");

     // Periodically update monitors
     var intervalId = setInterval(function () {
       $.post("Home/Progress", { id: taskId }, function (progress) {
         if (progress >= 100) {
           updateMonitor(taskId, "Completed");
         clearInterval(intervalId);
         } else {
           updateMonitor(taskId, progress + "%");
         }
       });
     }, 100);
   });
 });

});