将 cpu-bound/io-bound long-运行 代码包装到(异步)任务中

Wrapping both cpu-bound/io-bound long-running code into (async) Task

考虑简单的 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 队列(我们称之为 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 调用。说 'specific' 我的意思是我们使用第三方手段来做到这一点。这个调用是同步的并且很长。当处理结束时,消费者向请求消息的响应队列发送响应消息。

  3. 当响应到达 M 的响应队列时,是时候进行解析并将结果 return 发送给控制器了。

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

据我了解运行宁任务利用并行化。而使用 async-await 对会使 运行ning 任务异步。如果多个任务并行 运行 可能会有所帮助。

reasonable/possible 是否与 Tasking/Asynchrony 合并?如果是,那我从哪里开始呢?

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

例如,HTTP 客户端不会关心 RequestDocuments 是同步还是异步;无论哪种方式,HTTP 客户端都会发送请求并在稍后(即异步)收到响应。

同样,HTTP Web 服务器不关心Win32 服务是同步实现还是异步实现。它只知道将消息放入队列,一段时间后(即异步地)它从队列中获取响应消息。

As far as I understand running a Task makes use of parallelization. Whereas using the async-await pair makes the running task asynchronous.

有点。 Task 可用于异步或并行代码,这一事实引起了很多混乱。但是,Parallel 和 PLINQ 等任务并行库构造在并行(非异步)世界中是稳固的。

It could be helpful if several tasks would run in parallel.

我认为 "concurrently" 是合适的术语。

首先,请注意 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);
}