将cpu绑定/io绑定的长时间运行代码封装到(async)Task中
本文关键字:绑定 async Task 封装 运行 cpu io 长时间 代码 | 更新日期: 2023-09-27 18:27:02
考虑简单的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
在内部执行以下操作:
它向专用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>() ); }
该消息的使用者(Windows服务)进行特定的api调用。我所说的"具体"是指我们使用第三方手段来做到这一点。此调用是同步的,并且相当长。当处理结束时,使用者向请求消息的响应队列发送一条响应消息。
- 当响应到达M的响应队列时,需要解析并将结果返回给控制器
从最终用户的角度来看,这个任务应该是阻塞的,或者至少看起来应该是阻塞。
据我所知,运行任务会利用并行化。而使用async-await对则会使正在运行的任务异步。如果几个任务并行运行,可能会有所帮助。
在我的情况下,与任务/异步结合是否合理/可能?如果是,那么我从哪里开始?
网络调用的"异步性"对调用者是透明的。对于调用者来说,实现是同步的还是异步的并不重要。换句话说,从客户端的角度来看,它总是异步的。
例如,HTTP客户端根本不在乎RequestDocuments
是同步的还是异步的;无论哪种方式,HTTP客户端都将在一段时间后发送请求并接收响应(即异步)。
类似地,HTTPWeb服务器并不关心Win32服务是同步实现的还是异步实现的。它只知道它将一条消息放入队列,一段时间后(即异步)它从队列中获得一条响应消息。
据我所知,运行Task是利用并行化的。而使用异步等待对则使正在运行的任务异步。
有点。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);
}