将 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
在内部做这些事情:
它将请求发送到专用的 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 调用。说 'specific' 我的意思是我们使用第三方手段来做到这一点。这个调用是同步的并且很长。当处理结束时,消费者向请求消息的响应队列发送响应消息。
- 当响应到达 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);
}
考虑简单的 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 调用。说 'specific' 我的意思是我们使用第三方手段来做到这一点。这个调用是同步的并且很长。当处理结束时,消费者向请求消息的响应队列发送响应消息。
- 当响应到达 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);
}