【问题标题】:Wrapping both cpu-bound/io-bound long-running code into (async) Task将 cpu-bound/io-bound 长时间运行的代码包装到(异步)任务中
【发布时间】:2016-03-21 09:19:46
【问题描述】:

考虑简单的 MVC5 控制器:

public class DocumentsController {

    // ctor code is omitted

    [HttpPost, Route("api/documents/request/stamp={stamp}")]
    public ActionResult RequestDocuments(string stamp) {

        var documents = this.DocumentsRequestService.RequestByStamp(stamp);
        return new JsonResult(documents);
    }
}

DocumentsRequestService 在内部做这些事情:

  1. 它向一个专用的MSMQ-queue发送一个请求(我们称之为M)并且同步在M 的响应队列:

    using(var requestMessage = new Message()) {
    
        requestMessage.Body = documentStamp;
        requestMessage.Recoverable = true;
        requestMessage.Label = "request";
        requestMessage.ResponseQueue = this.requestedDocumentsResponseQueue;
        requestMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof(String) });
    
        // send request
    
        this.requestedDocumentsQueue.Send(requestMessage);
    
        // synchronously wait for response
    
        var responseMessage = this.requestedDocumentsResponseQueue.Receive();
    
        if(responseMessage.Label.EndsWith("success")) {
            return new DocumentsRequestResult(
                success: true,
                matches: parseMatchesList(responseMessage)
            );
        }
    
        return new DocumentsRequestResult(
            success: false,
            matches: Enumerable.Empty<DocumentsRequestMatch>()
        );
    }
    
  2. 该消息的使用者(Windows 服务)进行特定的 api 调用。通过说“具体”,我的意思是我们使用第三方手段来做到这一点。这个调用是同步的并且相当长。当处理结束时,消费者会向请求消息的响应队列发送响应消息。

  3. 当响应到达 M 的响应队列时,是时候解析并将结果返回给控制器了。

从最终用户的角度来看,这个任务应该是阻塞的,或者至少看起来应该是阻塞的。

据我了解,运行 Task 会利用并行化。而使用 async-await 对使正在运行的任务异步。如果多个任务并行运行会很有帮助。

在我的情况下与 Tasking/Asynchrony 结合是否合理/可能?如果是,那我从哪里开始?

【问题讨论】:

    标签: c# multithreading asynchronous task-parallel-library msmq


    【解决方案1】:

    网络调用的“异步”对调用者来说是透明的。对于调用者来说,实现是同步的还是异步的并不重要。换句话说,从客户端的角度来看,它始终是异步的。

    例如,HTTP 客户端不在乎RequestDocuments 是同步的还是异步的;无论哪种方式,HTTP 客户端都会在一段时间后发送请求并接收响应(即异步)。

    同样,HTTP Web 服务器也不关心 Win32 服务是同步实现还是异步实现。它只知道它把一条消息放在一个队列中,一段时间后(即异步)它从队列中得到一条响应消息。

    据我了解,运行任务会利用并行化。而使用 async-await 对会使正在运行的任务异步。

    有点。 Task 可用于异步或并行代码,这一事实引起了很多混乱。但是,诸如Parallel 和 PLINQ 之类的任务并行库结构在并行(非异步)的世界中占有一席之地。

    如果多个任务并行运行会很有帮助。

    我认为“同时”在这里是合适的术语。

    首先,请注意 ASP.NET 为您免费提供了大量并发。如果您想让每个请求在内部并发,那么您可以通过Task.WhenAll 轻松做到这一点。例如,您可以将 DocumentsRequestService 调用更改为异步调用(假设您的消息队列 API 支持异步调用):

    using(var requestMessage = new Message()) {
      ...
    
      // send request
      await this.requestedDocumentsQueue.SendAsync(requestMessage);
    
      // asynchronously wait for response
      var responseMessage = await this.requestedDocumentsResponseQueue.ReceiveAsync();
    
      ...
    }
    

    然后您可以从单个控制器操作中同时多次调用它:

    public async Task<ActionResult> RequestDocuments(string stamp1, string stamp2) {
      var task1 = this.DocumentsRequestService.RequestByStampAsync(stamp1);
      var task2 = this.DocumentsRequestService.RequestByStampAsync(stamp2);
      var documents = await Task.WhenAll(task1, task2);
      return new JsonResult(documents);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-10
      • 2019-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-15
      • 2017-10-24
      • 1970-01-01
      相关资源
      最近更新 更多