VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > Python基础教程 >
  • 运用示例简单讲解C#取消令牌CancellationTokenSource

前言

    相信大家在使用C#进行开发的时候,特别是使用异步的场景,多多少少会接触到CancellationTokenSource。看名字就知道它和取消异步任务相关的,而且一看便知大名鼎鼎的CancellationToken就是它生产出来的。不看不知道,一看吓一跳。它在取消异步任务、异步通知等方面效果还是不错的,不仅好用而且够强大。无论是微软底层类库还是开源项目涉及到Task相关的,基本上都能看到它的身影,而微软近几年也是很重视框架中的异步操作,特别是在.NET Core上基本上能看到Task的地方就能看到CancellationTokenSource的身影。这次我们抱着学习的态度,来揭开它的神秘面纱。

简单示例

相信对于CancellationTokenSource基本的使用,许多同学已经非常熟悉了。不过为了能够让大家带入文章的节奏,我们还是打算先展示几个基础的操作,让大家找找感觉,回到那个熟悉的年代。

基础操作

首先呈现一个最基础的操作。

1

2

3

4

5

6

7

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就会被取消了,操作如下

1

2

3

4

5

6

7

8

//设置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方法,具体使用如下

1

2

3

4

5

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

cancellationTokenSource.Token.Register(() => System.Console.WriteLine("我被取消了."));

//五秒之后取消

cancellationTokenSource.CancelAfter(5000);

System.Console.WriteLine("不会阻塞,我会执行.");

这个操作也是定时取消操作,需要注意的是CancelAfter方法并不会阻塞执行,所以打印的结果是

不会阻塞,我会执行.
我被取消了.

关联取消

还有的时候是这样的场景,就是我们设置一组关联的CancellationTokenSource,我们期望的是只要这一组里的任意一个CancellationTokenSource被取消了,那么这个被关联的CancellationTokenSource就会被取消。说得通俗一点就是,我们几个当中只要一个不在了,那么你也可以不在了,具体的实现方式是这样的

1

2

3

4

5

6

7

8

9

10

11

12

//声明几个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的属性,具体使用方式如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

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内部异常的捕获方式,否则对外可能还没有感知到具体异常的原因,它的使用方式是这样的,这里为了演示方便我直接换了一种更直接的方式

1

2

3

4

5

6

7

8

9

10

11

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的源码,看看它的工作机制是啥。本文贴出的源码是博主精简过的,毕竟源码太多不太可能全部粘贴出来,主要是跟着它的思路了解它的工作方式。

构造入手

因为这一次呢CancellationTokenSource的初始化函数中有一个比较重要的构造函数,那就是可以设置定时超时的操作,那么我们就从它的构造函数入手


相关教程