异步编程的取消机制 竟然还有这样的效果, 要不要了解一下?
异步任务监视令牌,以查看请求是否已经被取消。
如果请求取消,则应停止执行正在执行的操作。.NET 中的大多数异步方法将具有接受取消令牌的重载。
本文所说的请求是,耗时长的服务端读取查询(返回数据但不修改数据的查询)。取消已修改数据的请求对于用程序可能不是一个好的选择:
? ? -? 是否真的要因用户导航到应用程序中的另一个页面而取消保存?也许可以,但也可能不会。
? ?-? 除了数据问题,这也不会提高性能,因为数据库服务器将需要回滚该事务,这可能是一项昂贵的操作。
ASP.NET Core 实践
监测 CancellationToken 令牌
访问?MyReallySlowReport 页面,等待 5s,最终他们放弃了,去了其他页面:
所有正在进行的请求都将被取消。
MVC/WebAPI 能接受到取消请求的信号。开发者只需要在 Controller Action 中添加 CancellationToken 参数,并在后续行为中监测该取消信号。
浏览器取消请求时,ASP.NET Core 根据CancellationTokenModelBinder自动将 HttpContext.RequestAborted 这个 token 绑定到 Action 的 CancellationToken 参数,CancellationTokenModelBinder 将会在调用 AddMvc()或 services.AddMvcCore()时被注入。?
public async Task<ActionResult> MyReallySlowReport(CancellationToken cancellationToken)
{
List<ReportItem> items;
using (ApplicationDbContext context = new ApplicationDbContext())
{
items = await context.ReportItems.ToListAsync(cancellationToken);
}
return View(items);
}
以上代码显示: 很容易取消框架内置 API 的异步方法 (上面以 SQL 查询行为为例),因为上述 EF 的调用 api 支持取消异步操作;
对于自定义的长耗时查询行为,可以使用 CancellationToken 的原生触发方法:
public async Task<ActionResult> MyReallySlowReport(CancellationToken cancellationToken)
{
List<ReportItem> items;
using (ApplicationDbContext context = new ApplicationDbContext())
{
items = await context.ReportItems.ToListAsync(cancellationToken);
}
foreach (var item in items)
{
cancellationToken.ThrowIfCancellationRequested();
// slow non-cancellable work
Thread.Sleep(1000);
}
return View(items);
}
从以上代码也可推断出: CancellationToken 适用于同步方法。
处理取消操作抛出的异常?
Web 服务器触发取消信号,一般会向上会抛出?OperationCanceledException 或者?TaskCancellationException,所以为了记录这种非常规异常,建议采用独立的 ExceptionFilter 记录。
public class OperationCancelledExceptionFilter : ExceptionFilterAttribute
{
private readonly ILogger _logger;
public OperationCancelledExceptionFilter(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<OperationCancelledExceptionFilter>();
}
public override void OnException(ExceptionContext context)
{
if(context.Exception is OperationCanceledException)
{
_logger.LogInformation("Request was cancelled");
context.ExceptionHandled = true;
context.Result = new StatusCodeResult(400);
}
}
}
SPA 应用
以上是后端程序员利用 取消机制 缓解后台性能压力,针对的是浏览器触发的取消操作。 从 web 请求的产生来源分析,除了浏览器能发起请求, 编程也可以发起请求
? 想想日益常见的 SPA 程序(单页面程序),绝大部分页面请求都是 ajax 请求,你点击应用的另外一个“页面(JS 代码维护页面导航),浏览器不会自动取消请求。
在 SPA 应用中,前端发起的 ajax 请求, 若要取消,是需要以编程方式来完成的。
所以我墙裂建议,在 2C 的 SPA 应用中,切换“页面“”时请终止原页面的所有 ajax 请求。
评论