博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
.NET Core 取消令牌:CancellationToken
阅读量:4033 次
发布时间:2019-05-24

本文共 3326 字,大约阅读时间需要 11 分钟。

在 .NET 开发中,CancellationToken(取消令牌)是一项比较重要的功能,掌握并合理的使用 CancellationToken 可以提升服务的性能。特别在异步编程中,我常常会以创建 Task 的方式利用多线程执行一些耗时或非核心业务逻辑,表面上看既提高了整个流程的执行速度,又充分利用了服务器资源。然而类似 Task 的方式如果没设置过取消令牌,一旦开启,是无法被外部取消的,所以当主线程出异常或被提前终止时,已开启的异步线程其实依然在执行,这时对服务器资源可能是一种浪费,而 CancellationToken 就可以对这类情况进行一定的补救。

下面通过几种常见的使用场景来介绍 CancellationToken。

在 HttpClient 中的使用

HttpClient 是开发中比较常用的一个组件,关于超时可通过 Timeout 参数进行设置,其实它也是可以通过配置 CancellationToken 来实现超时定义,使用 CancellationToken 的最大好处是调用链共享此令牌状态,状态变更时会自动做出响应。

public async Task
GetHomeAsync(CancellationToken cancellationToken = default){ var client = _httpClientFactory.CreateClient(); var response = await client.GetAsync("https://github.com/", cancellationToken); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync();}
public async Task
GetGithubHome(){ var cts = new CancellationTokenSource(1000); var result = await _githubService.GetHomeAsync(cts.Token); return result;}

Github 一般访问会比较慢,可通过设置 1s 演示效果:

在 gRPC 中的使用

通过 VS 的 gRPC 服务模板创建一个 gRPC 服务端(如果对 gRPC 的使用还不太了解,参考官方文档[1] 玩起来吧),服务端主要提供一个获取用户列表 (GetList) 的接口。实现如下,_userRepository 内部是基于 MongoDB 实现的查询用户数据,对应使用的 MongoDB.Driver 提供的方法默认已支持设置 CancellationToken,所以这里直接引用 ServerCallContext  上下文中的 CancellationToken,而此 CancellationToken 又是从客户端传递来的,所以 CancellationToken  将作用于整个调用链中。另外如果在客户端动态取消了此令牌,服务器也将会收到通知。

public override async Task
GetList(GetListRequest request, ServerCallContext context){ await Task.Delay(1000); // 模拟效果,服务端停1s var users = await _userRepository.GetListAsync(context.CancellationToken); var reply = new GetListReply(); foreach (var item in users) { reply.Users.Add(new UserModel { UserId = item.UserId, Name = item.Name }); } return reply;}

Client 端主要代码如下,在接口层创建了 CancellationTokenSource 对象,并设置了令牌的过期时间,即在发起远程调用时,如果 1s 内没返回,那就取消这个调用。

public class UserService : IUserService{  private readonly UserClient _client;  public UserService()  {    var channel = GrpcChannel.ForAddress("https://localhost:5001");    _client = new UserClient(channel);  }  public async Task
GetListAsync(CancellationToken cancellationToken = default) { return await _client.GetListAsync(new GetListRequest(), cancellationToken: cts.Token); }}
[HttpGet]public async Task
GetUserList(){ var cts = new CancellationTokenSource(1000); var result = await _userService.GetListAsync(cts.Token); return JsonConvert.SerializeObject(result.Users);}

在 WebAPI 中的使用

前端调用后端的接口一般是基于 Ajax 来实现,当浏览器网页被 连续 F5 刷新页面加载中被停止Ajax 请求被主动 abort 时,控制台 network 看板中会出现一些状态为 canceled 的请求,如下:

对于这类请求,客户端虽然主动放弃了,如果服务端没有相应处理,其实接口对应的后端程序还是在不停的执行,只是这个执行结果不会被使用而已,所以这其实是非常浪费服务器资源的。

实际上浏览器取消请求时,服务端会将 HttpContext.RequestAborted 中的 Token 绑定到 Action 的 CancellationToken 参数。我们只需在接口中增加参数 CancellationToken,并将其传入其他接口调用中,程序识别到令牌被取消就会自动放弃继续执行。

[HttpGet]public async Task
Index(CancellationToken cancellationToken){ try { await _userService.GetListAsync(cancellationToken); await Task.Delay(5000); // 等待期间,取消请求(Postman 即可模拟) await _githubService.GetHomeAsync(cancellationToken); } catch (Exception ex) { Console.WriteLine(ex.Message + Environment.NewLine + ex.StackTrace); } return "ok";}

对于 WebAPI  接口被取消调用的场景,特别是对于查询功能的接口,CancellationToken 的传递就显得尤为必要了,它能减少很多底层服务接口的无效调用。

最后针对取消令牌产生的异常需要收尾干净,一般像 WebAPI 项目可以使用自带的过滤器或具有 AOP 功能的组件,gRPC 服务可使用自带的拦截器功能。

转载地址:http://ttkdi.baihongyu.com/

你可能感兴趣的文章
gstreamer插件之 videotestsrc
查看>>
autoupdate script
查看>>
linux 驱动开发 头文件
查看>>
/etc/resolv.conf
查看>>
container_of()传入结构体中的成员,返回该结构体的首地址
查看>>
linux sfdisk partition
查看>>
ipconfig,ifconfig,iwconfig
查看>>
opensuse12.2 PL2303 minicom
查看>>
电平触发方式和边沿触发的区别
查看>>
网络视频服务器移植
查看>>
Encoding Schemes
查看>>
移植QT
查看>>
如此调用
查看>>
计算机的发展史
查看>>
带WiringPi库的交叉编译如何处理一
查看>>
带WiringPi库的交叉笔译如何处理二之软链接概念
查看>>
Spring事务的七种传播行为
查看>>
ES写入找不到主节点问题排查
查看>>
Java8 HashMap集合解析
查看>>
ArrayList集合解析
查看>>