【问题标题】:Run task on background but return response to client in ASP MVC web application在后台运行任务,但在 ASP MVC Web 应用程序中向客户端返回响应
【发布时间】:2014-07-18 15:34:17
【问题描述】:

我正在尝试实现一个功能,其中 SQL Server 上有一个必须从 ASP MVC 应用程序调用并在后台处理的存储过程(它可能需要很长时间,因为它远程调用另一个存储过程来处理 excel存储在服务器上的文件)。但是最后一个 HTTP 请求的响应应该返回给客户端,这样 UI 就不会挂起等待处理。

我尝试了很多不同的方法,但 UI 仍然没有立即响应。

我试过BackgroundWorker,但它不允许主线程响应客户端,直到它完成处理, 我也试过了:

Thread.QueueUserWorkItem(delegate { //method which performs stored procedure calls//
}); 

它仍然没有返回响应并且HttpContext.Current 在后台线程中不可用。

也许有一种方法可以启动后台处理,暂停它以让主线程向浏览器返回响应,然后恢复后台线程以使用存储过程调用进行所有处理?

我错过了什么吗?

有人可以告诉我如何解决这个问题吗?将不胜感激。

【问题讨论】:

  • 我建议不要尝试使用 HTTP 上下文之类的东西。你显然需要它的东西,所以将任何东西作为线程状态传递到你的后台线程并进行处理。这不仅更简单,而且通过定义您的线程需要什么,您可以更好地定义它的目的。

标签: c# asp.net-mvc multithreading


【解决方案1】:

我最终得到了什么,它在我的情况下运行良好。我不确定效率,但它非常有效。因此,调用存储过程的代码我放在一个单独的线程上,这样主线程就完成了,而后台调用的处理发生在一个单独的线程上,并在一段时间后成功完成。同时 UI 可用,因此用户可以发出另一个请求,该请求也将以相同的方式处理。我测试了三个请求。一个接一个的重叠,这意味着在后台处理第一个请求时,我提交了另一个请求和另一个请求。 UI 立即响应,所有工作都已完成。

// ...main thread is working here
    //put a call to stored procedure on a separate thread
    Thread t = new Thread(()=> {
       //call stored procedure which will run longer time since it calls another remote stored procedure and
       //waits until it's done processing
    });
    t.Start();

   // ...main thread continue to work here and finishes the request so it looks for user as the response is coming right away, all other stuff is being processed on that new thread so user even doesn't suspect

【讨论】:

  • 出现错误“无法将类型 void 隐式转换为线程”
【解决方案2】:

我将引用 Stephan Clearys great article:

当您在服务器端使用异步(例如,使用 ApiController)时,您可以将每个 Web 请求视为异步操作。 但是当你yield时,你只yield给web服务器线程池,而不是给客户端。HTTP只允许单个响应,所以只有在请求完全完成时才能发送响应。 p>

基本上,这不符合 HTTP 协议,每个请求只有一个响应。

这可以通过多次调用 ASP.NET 服务来实现,其中一个请求会立即返回一个唯一 ID,客户端可以多次查询该 ID 以了解进度。您可以查看SignalR 以获取有关此类实现的帮助:

什么是 SignalR 和“实时网络”功能?它是让您的服务器端代码实时将内容推送到连接的客户端的能力。

【讨论】:

  • 现在,有了 HTTP/2,我们有了服务器推送,因此通信不再需要严格遵循请求/响应模式
【解决方案3】:

Dino Esposito 的 part1part2 文章概述了一种使用客户端计时器和控制器操作实现轮询的方法。您基本上会序列化对返回任务状态和完成数据的进度工作控制器方法的访问。但是,如果您只打算执行一两个长时间运行的进程,这可能会有点麻烦。

【讨论】:

    【解决方案4】:

    如果对客户端的响应不依赖于后台进程的结果(即在后台运行进程并且没有 UI 等待它),那么您可以使用 Revalee(一个开源工具)来执行后台任务。

    UI 将请求此路由...

    public class ForegroundController : Controller
    {
      public ActionResult InitiateBackgroundTask()
      {
        // The absolute URL that will be requested on the callback.
        var callbackUri = new Uri("http://localhost/BackgroundTask/Callback");
    
        // The information that will be needed to initiate the background task.
        object state = "Any object";
    
        this.CallbackNowAsync(callbackUri, state)
    
        // ... your controller will now return a response to the browser
        return View();
      }
    }
    

    后台工作将在此控制器中执行...

    public class BackgroundTaskController : Controller
    {
      [AllowAnonymous]
      [HttpPost]
      [CallbackAction]
      public ActionResult Callback(Guid callbackId, object state)
      {
        // Perform the background work
    
        // ... insert your background task code here ...
    
        // Return a status code back to the Revalee Service.
        return new HttpStatusCodeResult(HttpStatusCode.OK);
      }
    }
    

    Revalee Project Site

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-04
      • 1970-01-01
      • 1970-01-01
      • 2018-03-09
      • 1970-01-01
      相关资源
      最近更新 更多