C# CancellationToken和CancellationTokenSource的用法详解
作者:没有星星的夏季 发布时间:2023-05-26 19:39:41
CancellationToken
CancellationToken有一个构造函数,可以传入一个bool类型表示当前的CancellationToken是否是取消状态。另外,因为CancellationToken是一个结构体,所以它还有一个空参数的构造函数。
public CancellationToken();//因为是结构体,才有空构造函数,不过没什么作用
public CancellationToken(bool canceled);
属性如下:
//静态属性,获取一个空的CancellationToken,这个CancellationToken注册的回调方法不会被触发,作用类似于使用空构造函数得到的CancellationToken
public static CancellationToken None { get; }
//表示当前CancellationToken是否可以被取消
public bool CanBeCanceled { get; }
//表示当前CancellationToken是否已经是取消状态
public bool IsCancellationRequested { get; }
//和CancellationToken关联的WaitHandle对象,CancellationToken注册的回调方法执行时通过这个WaitHandle实现的
public WaitHandle WaitHandle { get; }
常用方法:
//往CancellationToken中注册回调
public CancellationTokenRegistration Register(Action callback);
public CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext);
public CancellationTokenRegistration Register([NullableAttribute(new[] { 1, 2 })] Action<object?> callback, object? state);
public CancellationTokenRegistration Register([NullableAttribute(new[] { 1, 2 })] Action<object?> callback, object? state, bool useSynchronizationContext);
//当CancellationToken处于取消状态是,抛出System.OperationCanceledException异常
public void ThrowIfCancellationRequested();
常用的注册回调的方法是上面4个Register方法,其中callback是回调执行的委托,useSynchronizationContext表示是否使用同步上下文,state是往回调委托中传的参数值
另外,Register方法会返回一个CancellationTokenRegistration结构体,当注册回调之后,可以调用CancellationTokenRegistration的Unregister方法来取消注册,这个Unregister方法会返回一个bool值,当成功取消时返回true,当取消失败(比如回调已执行)将返回false:
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
var cancellationTokenRegistration = cancellationTokenSource.Token.Register(() =>
{
Console.WriteLine("Canceled");//这里将不会执行输出
});
//cancellationTokenSource.Cancel();
//var result = cancellationTokenRegistration.Unregister();//result = false
var result = cancellationTokenRegistration.Unregister();//result = true
cancellationTokenSource.Cancel();
上面提到,CancellationToken可以使用构造函数直接构造,同时可以传入一个参数,表示当前的状态,需要注意的是,CancellationToken的状态最多可以改变一次,也就是从未取消变成已取消。
如果构造时传入true,也就是说CancellationToken是已取消状态,这个时候注册的回调都会立即执行:
CancellationToken cancellationToken = new CancellationToken(true);
cancellationToken.Register(() =>
{
Console.WriteLine("Canceled");//这里会立即执行输出Canceled
});
但如果构造时传入的是false,说明CancellationToken处于未取消状态,这时候注册的回到都会处于一个待触发状态:
CancellationToken cancellationToken = new CancellationToken(false);
cancellationToken.Register(() =>
{
Console.WriteLine("Canceled");//这里不会立即执行输出
});
通过Register方法注册的服务只会执行一次!
但一般的,如果传入false构造出来的CancellationToken,可以认为是不会触发的,因为它没有触发的方法!所以一般的,我们都不会直接使用构造函数创建CancellationToken,而是使用CancellationTokenSource对象来获取一个CancellationToken
CancellationTokenSource
CancellationTokenSource可以理解为CancellationToken的控制器,控制它什么时候变成取消状态的一个对象,它有一个CancellationToken类型的属性Token,只要CancellationTokenSource创建,这个Token也会被创建,同时Token会和这个CancellationTokenSource绑定
//表示Token是否已处于取消状态
public bool IsCancellationRequested { get; }
//CancellationToken 对象
public CancellationToken Token { get; }
可以直接创建一个CancellationTokenSource对象,同时指定一个时间段,当过了这段时间后,CancellationTokenSource就会自动取消了。
CancellationTokenSource的取消有4个方法:
//立刻取消
public void Cancel();
//立刻取消
public void Cancel(bool throwOnFirstException);
//延迟指定时间后取消
public void CancelAfter(int millisecondsDelay);
//延迟指定时间后取消
public void CancelAfter(TimeSpan delay);
Cancel和两个CancelAfter方法没什么特别的,主要就是有一个延迟的效果,需要注意的是Cancel的两个重载之间的区别。
首先,上面说道,CancellationToken状态只能改变一次(从未取消变成已取消),当CancellationToken时已取消状态时,每次往其中注册的回调都会立刻执行!当处于未取消状态时,注册进去的回调都会等待执行。
需要注意的是,当在未取消状态 * 册多个回调时,它们在执行时是一个类似栈的结构顺序,先注册后执行。
而CancellationToken的Register可以注册多个回调,那他们可能都会抛出异常,throwOnFirstException参数表示在第一次报错时的处理行为.
throwOnFirstException = true 表示立即抛出当前发生的异常,后续的回调将会取消执行
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
try
{
cancellationTokenSource.Token.Register(() =>
{
throw new Exception("1");
});
cancellationTokenSource.Token.Register(() =>
{
throw new Exception("2");//不会执行
});
cancellationTokenSource.Cancel(true);
}
catch (Exception ex)
{
//ex is System.Exception("1")
}
throwOnFirstException = false 表示跳过当前回调的异常,继续执行生效的回调,等所有的回调执行完成之后,再将所有的异常打包成一个System.AggregateException异常抛出来!
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
try
{
cancellationTokenSource.Token.Register(() =>
{
throw new Exception("1");
});
cancellationTokenSource.Token.Register(() =>
{
throw new Exception("2");
});
cancellationTokenSource.Cancel(false);//相当于cancellationTokenSource.Cancel()
}
catch (Exception ex)
{
//ex is System.AggregateException:[Exception("2"),Exception("1")]
}
CancellationTokenSource还可以与其它CancellationToken关联起来,生成一个新的CancellationToken,当其他CancellationToken取消时,会自动触发当前的CancellationTokenSource执行取消动作!
使用场景一
当我们创建异步操作时,可以传入一个CancellationToken,当异步操作处于等待执行状态时,可以通过设置CancellationToken为取消状态将异步操作取消执行:
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
var task = new Task(() =>
{
Thread.Sleep(1500);//执行了2秒中代码
Console.WriteLine("Execute Some Code");
}, cancellationTokenSource.Token);
task.Start();//启动,等待调度执行
//发现不对,可以取消task执行
cancellationTokenSource.Cancel();
Thread.Sleep(1000);//等待1秒
Console.WriteLine("Task状态:" + task.Status);//Canceled
但是经常的,我们的取消动作可能不会那么及时,如果异步已经执行了,再执行取消时无效的,这是就需要我们自己在异步委托中检测了:
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
var task = new Task(() =>
{
Thread.Sleep(1500);//执行了2秒中代码
cancellationTokenSource.Token.ThrowIfCancellationRequested();
Console.WriteLine("Execute Some Code");
}, cancellationTokenSource.Token);
task.Start();//启动,等待调度执行
Thread.Sleep(1000);////一段时间后发现不对,可以取消task执行
cancellationTokenSource.Cancel();
Thread.Sleep(1000);//等待1秒
Console.WriteLine("Task状态:" + task.Status);//Canceled
使用场景二
有时,我们希望在触发某个时间后,可以执行某些代码功能,但是在异步环境下,我们不能保证那些要执行的代码是否已准备好了,比如我们有一个Close方法,当调用Close后表示是关闭状态,如果我们相当程序处于关闭状态时执行一些通知,一般的,我们可能是想到采用事件模型,或者在Close方法传入事件委托,或者采用一些诸如模板设计这样的模型去实现:
class Demo
{
public void Close(Action callback)
{
//关闭
Thread.Sleep(3000);
callback?.Invoke();//执行通知
}
}
或者
class Demo
{
public event Action Callback;
public void Close()
{
//关闭
Thread.Sleep(3000);
Callback?.Invoke();//执行通知
}
}
但是这就有问题了,如果是传入参数或者采用事件模型,因为前面说过了,如果在异步环境下,我们不能保证那些要执行的代码是否已准备好了,也许在执行Close方法时,程序还未注册回调。
这个时候就可以使用CancellationToken来解决这个问题:
主需要往Token属性中注册回调而无需关注Close什么时候执行了
使用场景三
有时候,我们写一个异步无限循环的方法去处理一些问题,而我们希望可以在方法外来停止它这个时候,我们就可以通过返回CancellationTokenSource来实现了:
public CancellationTokenSource Listen()
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
//循环调度执行
Task.Run(() =>
{
while (true)
{
cancellationTokenSource.Token.ThrowIfCancellationRequested();
//循环执行一些操作
Thread.Sleep(1000);
Console.WriteLine("Run");
}
});
return cancellationTokenSource;
}
来源:https://www.cnblogs.com/shanfeng1000/p/13402152.html
猜你喜欢
- /// 构造随机数 种子static int GetRandomSeed(){ byte[] byt
- 目前知道的情况被调用的C/C++函数只能是全局函数 不能调用类中的成员方法被调用的C函数必须使用extern “C“包含,保证采用的导出函数
- 一、面向对象的描述面向对象是一种现在最为流行的程序设计方法,几乎现在的所有应用都以面向对象为主了,最早的面向对象的概念实际上是由IBM提出的
- C#之委托委托:顾名思义,让别人帮你办件事。委托是C#实现回调函数的一种机制。可能有人会问了,回调函数是个啥???举个例子:我现在是一家公司
- IDEA 初使用昨天,我在某位大神的推荐下,下载了idea编辑器,同时被其强大的功能所震撼。此篇文章去帮助新手小白,来安装并,解决idea安
- ealsticsearch只是后端提供各种api,那么怎么直观的使用它呢?elasticsearch-head将是一款专门针对于elasti
- Spring Boot 2.x 已经发布了很久,现在 Spring Cloud 也发布了 基于 Spring Boot 2.x 的 Finc
- 本文实例为大家分享了java实现砸金蛋抽奖的具体代码,供大家参考,具体内容如下代码如下需求:用户每一次砸金蛋,抽中一等奖的概率为2% 二等奖
- 一、构造方法类的构造方法是类的成员方法的一种,它的作用是对类中的成员进行初始化操作。类的构造方法分为:
- C++11 引入一个全新的线程库,包含启动和管理线程的工具,提供了同步(互斥、锁和原子变量)的方法,我将试图为你介绍这个全新的线
- Zuul作为网关服务,是其他各服务对外中转站,通过Zuul进行请求转发。这就涉及到部分数据是不能原封返回的,比如服务之间通信的凭证,用户的加
- 判断字符串是否为IP地址通常都是基于正则表达式实现的,无论是引入外部的依赖包亦或是自己写正则实现,基本都是基于正则表达式实现的判断。然而比较
- 1.稀疏数组引入1.1 使用场景笔者在课程设计中曾写过一个扫雷小游戏,为了便于讲解,我们来做个简化(实际比这个复杂),只考虑当前位置有雷与无
- 这是之前软工课设我写的java访问mysql工具类,它经过了多轮的测试,应该能够适应大多数的操作需求。比之前大二写的更鲁棒,更易用。pack
- 本文介绍了SpringCloud +Zookeeper完成配置中心,分享给大家,具有如下:使用场景项目配置更改不需要打包,重启提供配置文件的
- 本文实例为大家分享了Android登录注册功能的具体代码,供大家参考,具体内容如下展示效果代码区MainActivity(登录方法)publ
- 可以理解当我们要调用一个方法时,我们会把指定的数值,传递给方法中的参数,这样方法中的参数就拥有了这个指定的值,可以使用该值,在方法中运算了。
- 问题之前项目能够正常运行,因为默认选择db0,后来新的需求来了,不是默认db0,而是给参数选择db。修改后代码如下,却报错NOAUTH Au
- 1.介绍有时候我们在Linux中运行Java程序时,需要调用一些Shell命令和脚本。而Runtime.getRuntime().exec(
- 学习编程语言最重要的就是实践。很多小伙伴在学习完编程语言后,一直停留在基础阶段,只懂一大堆理论知识,而不懂得实践。那么,今天我们一起来动手做