浅谈C#取消令牌CancellationTokenSource
Posted yi念之间
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈C#取消令牌CancellationTokenSource相关的知识,希望对你有一定的参考价值。
前言
相信大家在使用C#进行开发的时候,特别是使用异步的场景,多多少少会接触到CancellationTokenSource。看名字就知道它和取消异步任务相关的,而且一看便知大名鼎鼎的CancellationToken就是它生产出来的。不看不知道,一看吓一跳。它在取消异步任务、异步通知等方面效果还是不错的,不仅好用而且够强大。无论是微软底层类库还是开源项目涉及到Task相关的,基本上都能看到它的身影,而微软近几年也是很重视框架中的异步操作,特别是在.NET Core上基本上能看到Task的地方就能看到CancellationTokenSource的身影。这次我们抱着学习的态度,来揭开它的神秘面纱。
简单示例
相信对于CancellationTokenSource基本的使用,许多同学已经非常熟悉了。不过为了能够让大家带入文章的节奏,我们还是打算先展示几个基础的操作,让大家找找感觉,回到那个熟悉的年代。
基础操作
首先呈现一个最基础的操作。
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = cancellationTokenSource.Token;
cancellationToken.Register(() => System.Console.WriteLine("取消了???"));
cancellationToken.Register(() => System.Console.WriteLine("取消了!!!"));
cancellationToken.Register(state => System.Console.WriteLine($"取消了。。。{state}"),"啊啊啊");
System.Console.WriteLine("做了点别的,然后取消了.");
cancellationTokenSource.Cancel();
这个操作是最简单的操作,我们上面提到过CancellationTokenSource就是用来生产CancellationToken的,还可以说CancellationToken是CancellationTokenSource的表现,这个待会看源码的时候我们会知道为啥这么说。这里呢我们给CancellationToken
注册几个操作,然后使用CancellationTokenSource的Cancel方法
取消操作,这时候控制台就会打印结果如下
做了点别的,然后取消了.
取消了。。。啊啊啊
取消了!!!
取消了???
通过上面简单的示例,大家应该非常轻松的理解了它的简单使用。
定时取消
有的时候呢我们可能需要超时操作,比如我不想一直等着,到了一个固定的时间我就要取消操作,这时候我们可以利用CancellationTokenSource的构造函数给定一个限定时间,过了这个时间CancellationTokenSource就会被取消了,操作如下
//设置3000毫秒(即3秒)后取消
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(3000);
CancellationToken cancellationToken = cancellationTokenSource.Token;
cancellationToken.Register(() => System.Console.WriteLine("我被取消了."));
System.Console.WriteLine("先等五秒钟.");
await Task.Delay(5000);
System.Console.WriteLine("手动取消.")
cancellationTokenSource.Cancel();
然后在控制台打印的结果是这个样子的,活脱脱的为我们实现了内建的超时操作。
先等五秒钟.
我被取消了.
手动取消.
上面的写法是在构造CancellationTokenSource的时候设置超时等待,还有另一种写法等同于这种写法,使用的是CancelAfter
方法,具体使用如下
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.Token.Register(() => System.Console.WriteLine("我被取消了."));
//五秒之后取消
cancellationTokenSource.CancelAfter(5000);
System.Console.WriteLine("不会阻塞,我会执行.");
这个操作也是定时取消操作,需要注意的是CancelAfter
方法并不会阻塞执行,所以打印的结果是
不会阻塞,我会执行.
我被取消了.
关联取消
还有的时候是这样的场景,就是我们设置一组关联的CancellationTokenSource,我们期望的是只要这一组里的任意一个CancellationTokenSource被取消了,那么这个被关联的CancellationTokenSource就会被取消。说得通俗一点就是,我们几个当中只要一个不在了,那么你也可以不在了,具体的实现方式是这样的
//声明几个CancellationTokenSource
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationTokenSource tokenSource2 = new CancellationTokenSource();
CancellationTokenSource tokenSource3 = new CancellationTokenSource();
tokenSource2.Token.Register(() => System.Console.WriteLine("tokenSource2被取消了"));
//创建一个关联的CancellationTokenSource
CancellationTokenSource tokenSourceNew = CancellationTokenSource.CreateLinkedTokenSource(tokenSource.Token, tokenSource2.Token, tokenSource3.Token);
tokenSourceNew.Token.Register(() => System.Console.WriteLine("tokenSourceNew被取消了"));
//取消tokenSource2
tokenSource2.Cancel();
上述示例中因为tokenSourceNew关联了tokenSource、tokenSource2、tokenSource3所以只要他们其中有一个被取消那么tokenSourceNew也会被取消,所以上述示例的打印结果是
tokenSourceNew被取消了
tokenSource2被取消了
判断取消
上面我们使用的方式,都是通过回调的方式得知CancellationTokenSource被取消了,没办法通过标识去得知CancellationTokenSource是否可用。不过微软贴心的为我们提供了IsCancellationRequested
属性去判断,需要注意的是它是CancellationToken
的属性,具体使用方式如下
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = tokenSource.Token;
//打印被取消
cancellationToken.Register(() => System.Console.WriteLine("被取消了."));
//模拟传递的场景
Task.Run(async ()=> {
while (!cancellationToken.IsCancellationRequested)
{
System.Console.WriteLine("一直在执行...");
await Task.Delay(1000);
}
});
//5s之后取消
tokenSource.CancelAfter(5000);
上述代码五秒之后CancellationTokenSource被取消,因此CancellationTokenSource的Token也会被取消。反映到IsCancellationRequested上就是值为true说明被取消,为false说明没被取消,因此控制台输出的结果是
一直在执行...
一直在执行...
一直在执行...
一直在执行...
一直在执行...
被取消了.
还有另一种方式,也可以主动判断任务是否被取消,不过这种方式简单粗暴,直接是抛出了异常。如果是使用异步的方式的话,需要注意的是Task内部异常的捕获方式,否则对外可能还没有感知到具体异常的原因,它的使用方式是这样的,这里为了演示方便我直接换了一种更直接的方式
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = tokenSource.Token;
cancellationToken.Register(() => System.Console.WriteLine("被取消了."));
tokenSource.CancelAfter(5000);
while (true)
{
//如果操作被取消则直接抛出异常
cancellationToken.ThrowIfCancellationRequested();
System.Console.WriteLine("一直在执行...");
await Task.Delay(1000);
}
执行五秒之后则直接抛出 System.OperationCanceledException: The operation was canceled.
异常,异步情况下注意异常处理的方式即可。通过上面这些简单的示例,相信大家对CancellationTokenSource有了一定的认识,大概知道了在什么时候可以使用它,主要是异步取消通知,或者限定时间操作通知等等。CancellationTokenSource是个不错的神器,使用简单功能强大。
源码探究
通过上面的示例,相信大家对CancellationTokenSource有了一个基本的认识,真的是非常强大,而且使用起来也非常的简单,这也是c#语言的精妙之处,非常实用,让你用起来的时候非常舒服,有种用着用着就想跪下的冲动。步入正题,接下来让我们来往深处看看CancellationTokenSource的源码,看看它的工作机制是啥。本文贴出的源码是博主精简过的,毕竟源码太多不太可能全部粘贴出来,主要是跟着它的思路了解它的工作方式。
构造入手
因为这一次呢 以上是关于浅谈C#取消令牌CancellationTokenSource的主要内容,如果未能解决你的问题,请参考以下文章 C#:使用 CancellationToken 取消 MySqlCommand,给出 NULLReferenceExceptionCancellationTokenSource
的初始化函数中有一个比较重要的构造函数,那就是可以设置定时超时的操作,那么我们就从它的构造函数入手[点击查看源码