相关类型:

CancellationTokenSource 主要用来创建或取消令牌

CancellationToken 监听令牌状态,注册令牌取消事件

OperationCanceledException 令牌被取消时抛出的异常,可以由监听者自主决定是否抛出异常

CancellationTokenSource

创建令牌:

CancellationTokenSource cts = new CancellationTokenSource()

CancellationToken token=cts.Token;

取消释放令牌:

cts.Cancel();

CancellationToken

监听令牌取消事件:

token.Register(() => Console.WriteLine("令牌被取消"));

判断令牌是否取消

//返回一个bool,如果令牌被取消为true
token.IsCancellationRequested //如果token被取消则抛出异常,内部实现其实就是判断IsCancellationRequested
token.ThrowIfCancellationRequested()=>{
if(token.IsCancellationRequested){
throw new OperationCanceledException();
}
}

代码示例

下面模拟一个文件下载的任务,在未下载完成后下载任务被取消

 public void Run()
{
CancellationTokenSource cts = new CancellationTokenSource(); Task.Run(() =>
{
//等待两秒后取消,模拟的是用户主动取消下载任务
Thread.Sleep(2000);
cts.Cancel();
}); try
{
var size = DownloadFile(cts.Token);
Console.WriteLine("文件大小:" + size);
}
catch (OperationCanceledException)
{
Console.WriteLine("下载失败");
}finally{
cts.Dispose();
}
Thread.Sleep(2000);
} /// <summary>
/// 模拟下载文件,下载文件需要五秒
/// </summary>
/// <returns></returns>
public int DownloadFile(CancellationToken token)
{
token.Register(() =>
{
System.Console.WriteLine("监听到取消事件");
}); Console.WriteLine("开始下载文件");
for (int i = 0; i < 5; i++)
{
token.ThrowIfCancellationRequested();
Console.WriteLine(i.ToString());
Thread.Sleep(1000);
}
Console.WriteLine("文件下载完成");
return 100;
}

输出结果:

开始下载文件
0
1
监听到取消事件
下载失败

思考

为什么要将CancellationToken和CancellationTokenSource分为两个类呢,直接一个CancellationToken又可以取消又可以判断状态注册啥的不是更好,更方便?

其实每种类的设计和实现都可以有很多不同的策略,CTS和CT从这个两个类提供的为数不多的公开方法中就可以看出,CTS用来控制Token的生成和取消等生命周期状态,CT只能用来监听和判断,无法对Token的状态进行改变。

所以这种设计的目的就是关注点分离。限制了CT的功能,避免Token在传递过程中被不可控的因素取消造成混乱。

关联令牌

继续拿上面的示例来说,示例中实现了从外部控制文件下载功能的终止。

如果要给文件下载功能加一个超时时间的限制,此时可以增加一个控制超时时间的token,将外部传来的token和内部token 关联起来变为一个token

只需要将DownloadFile()函数做如下改造即可

public int DownloadFile(CancellationToken externalToken)
{
//通过构造函数设置TokenSource一秒之后调用Cancel()函数
var timeOutToken = new CancellationTokenSource(new TimeSpan(0, 0, 1)).Token;
using (var linkToken = CancellationTokenSource.CreateLinkedTokenSource(externalToken, timeOutToken))
{
Console.WriteLine("开始下载文件");
for (int i = 0; i < 5; i++)
{
linkToken.Token.ThrowIfCancellationRequested();
Console.WriteLine(i.ToString());
Thread.Sleep(1000);
}
Console.WriteLine("文件下载完成");
return 100;
}
}

此时不论是externalToken取消,或是timeOutToken取消,都会触发linkToken的取消事件

CancellationChangeToken

CancellationChangeToken主要用来监测目标变化,需配合ChangeToken使用。从功能场景来说,其实ChangeToken的功能和事件似乎差不多,当监控的目标发生了变化,监听者去做一系列的事情。

但是事件的话,监听者需要知道目标的存在,就是如果A要注册B的事件,A是要依赖B的。

CancellationChangeToken是基于CancellationToken来实现的,可以做到依赖于Token而不直接依赖被监听的类

创建CancellationChangeToken:

new CancellationChangeToken(new CancellationTokenSource().Token)

监听Token变动

new CancellationChangeToken(cts.Token).RegisterChangeCallback(obj => Console.WriteLine("token 变动"), null);

CancellationChangeToken只是把CancellationToken包装了一层。RegisterChangeCallback最终也是监听的CancellationToken的IsCancellationRequested状态。

所以就有个问题,代码写到这里,并不能实现每次内部变动都触发回调事件。

因为CT只会Cancel一次,对应的监听也会执行一次。无法实现多次监听

为了实现变化的持续监听,需要做两个操作

  • 让Token在Cancel之后重新初始化
  • 每次Cancel回调之后重新监听新的Token

先上代码,下面的代码实现了每次时间变动都会通知展示面板刷新时间的显示

public void Run()
{
var bjDate = new BeijingDate();
DisplayDate(bjDate.GetChangeToken, bjDate.GetDate);
Thread.Sleep(50000);
} public void DisplayDate(Func<IChangeToken> getChangeToken, Func<DateTime> getDate)
{
ChangeToken.OnChange(getChangeToken, () => Console.WriteLine("当前时间:" + getDate()));
} public class BeijingDate
{
private CancellationTokenSource cts;
private DateTime date;
public BeijingDate()
{
cts = new CancellationTokenSource();
var timer = new Timer(TimeChange, null, 0, 1000);
} private void TimeChange(object state)
{
date = DateTime.Now;
var old = cts;
cts = new CancellationTokenSource();
old.Cancel();
} public DateTime GetDate() => date;
public CancellationChangeToken GetChangeToken()
{
return new CancellationChangeToken(cts.Token);
}
}

在TimeChange()中修改了时间,重置了Token并将旧的Token取消

在DisplayDate中用ChangeToken.OnChange获取对应的Token并监听

实现了DisplayData函数和BeijingDate这个类的解耦

ChangeToken.OnChange 这个函数接收两个参数,一个是获取Token的委托,一个是Token取消事件的响应委托。

每次在处理完Token的取消事件后,他会重新调用第一个委托获取Token,而此时我们已经生成了新的Token,最终实现了持续监控

.Net中异步任务的取消和监控的更多相关文章

  1. 在内核中异步请求设备固件firmware的测试代码

    在内核中异步请求设备固件firmware的测试代码 static void ghost_load_firmware_callback(const struct firmware *fw, void * ...

  2. 【Android 基础】Android中全屏或者取消标题栏

    先介绍去掉标题栏的方法: 第一种:也一般入门的时候经常使用的一种方法 requestWindowFeature(Window.FEATURE_NO_TITLE);//去掉标题栏 注意这句一定要写在se ...

  3. 在 tornado 中异步无阻塞的执行耗时任务

    在 tornado 中异步无阻塞的执行耗时任务 在 linux 上 tornado 是基于 epoll 的事件驱动框架,在网络事件上是无阻塞的.但是因为 tornado 自身是单线程的,所以如果我们在 ...

  4. C#中异步和多线程的区别

    C#中异步和多线程的区别是什么呢?异步和多线程两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性.甚至有些时候我们就认为异步和多线程是等同的概念.但是,异步和多线程还是有一些区别的.而这些区 ...

  5. ionic中将service中异步返回的数据赋值给controller的$scope

    1.service中异步获取数据实例 angular.module('starter.services', []) .factory('Chats', function($http,$q) {//定义 ...

  6. nodejs中异步

    nodejs中的异步 1 nodejs 中的异步存在吗? 现在有点 javascript 基础的人都在听说过 nodejs ,而只要与 javascript 打交到人都会用或者是将要使用 nodejs ...

  7. cocos2dx lua中异步加载网络图片,可用于显示微信头像

    最近在做一个棋牌项目,脚本语言用的lua,登录需要使用微信登录,用户头像用微信账户的头像,微信接口返回的头像是一个url,那么遇到的一个问题就是如何在lua中异步加载这个头像,先在引擎源码里找了下可能 ...

  8. echarts异步数据加载(在下拉框选择事件中异步更新数据)

    接触echarts 大半年了,从不会到熟练也做过不少的图表,隔了一段时间没使用这玩意,好多东西真心容易忘了.在接触echarts这期间也没有总结什么东西,今天我就来总结一下如何在echart中异步加载 ...

  9. SpringBoot中异步请求和异步调用(看这一篇就够了)

    原创不易,如需转载,请注明出处https://www.cnblogs.com/baixianlong/p/10661591.html,否则将追究法律责任!!! 一.SpringBoot中异步请求的使用 ...

随机推荐

  1. DRF使用JWT进行用户认证

    1. 首先需要安装第三方依赖包 pip install djangorestframework-jwt 2. 在Django的settings文件中 配置全局的JWT认证类 REST_FRAMEWOR ...

  2. 第三篇 -- HTML基础

    买的课程学习 Web 前端标准 web前端技术指的不是某一项技术,而是一系列技术的集合,主要包括: html -- 结构标准:负责网页结构的搭建 css -- 样式标准/表现标准:负责网页的美化工作 ...

  3. js之检测浏览器

    getBrowser () { let ua = navigator.userAgent.toLocaleLowerCase() let browserType = null if (ua.match ...

  4. 大数据学习(12)—— Hive Server2服务

    什么是Hive Server2 上一篇我们启动了hive --service metastore服务,可以通过命令行来访问hive服务,但是它不支持多客户端同时访问,参见官网说明:HiveServer ...

  5. Java进阶练习题整理(1)

    1. 检查时异常.非检查时异常.运行时异常. Throwable是一切异常.错误类的超类,Throwable有两个子类,Error(错误)和Exception(异常).Exception异常又分为Ru ...

  6. Sth about Educational DP Contest

    Contest Website : atcoder.jp/contests/dp \[\begin{array}{c|C|c|c} TaskNum & TaskName & Statu ...

  7. 优化SQL 查询性能

    为什么查询会很慢 如果把查询看作是一个任务,那么它由一系列子任务组成,每个子任务都会消耗一定的时间.要优化查询,实际上是要优化其子任务,要么消除其中一些子任务,要么减少子任务的执行次数,要么让子任务运 ...

  8. postman之断言

    1 (状态码断言)和(返回内容断言)

  9. 【Lua篇】静态代码扫描分析(三)语法分析

    一.语法分析 通过将词法分析获取的Token流按照目标语言的语法进行解析的过程,例如解析函数声明.函数调用.变量声明.各种语句等. 二.Lua语法分析 在写语法分析程序前,先需要了解Lua的语句和语法 ...

  10. EL表达式和JSTL标签

    什么是 EL 表达式,EL 表达式的作用? EL 表达式的全称是:Expression Language.是表达式语言. EL 表达式的什么作用:EL 表达式主要是代替 jsp 页面中的表达式脚本在 ...