前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2019-1-24-Task真的取消了么

2019-1-24-Task真的取消了么

作者头像
黄腾霄
发布2020-06-10 11:28:43
5770
发布2020-06-10 11:28:43
举报
文章被收录于专栏:黄腾霄的博客

我们都知道Task.Run方法可以传入一个CancellationToken,用于取消。可是有多少人真的去了解过当调用CancellationSource.Cancel方法时,Task是否真的被取消了


我们做个实验

代码语言:javascript
复制
        public static async void Foo()
        {
            var source = new CancellationTokenSource();

            Task.Run(() =>
            {
                Thread.Sleep(TimeSpan.FromSeconds(5));
                Console.WriteLine("task 运行结束");
            }, source.Token);
            await Task.Delay(TimeSpan.FromSeconds(3));
            source.Cancel();
            Console.WriteLine("取消任务");
        }

创建上述程序,让一个Task运行5秒后输出”task运行结束”,而在3秒后就进行取消。

令人惊讶的是任务竟然顺利执行完成。

为了进一步确认结果,我们添加一个后续任务,查看下之前任务的结束状态

代码语言:javascript
复制
        public static async void Foo()
        {
            var source = new CancellationTokenSource();

            Task.Run(() =>
            {
                Thread.Sleep(TimeSpan.FromSeconds(5));
                Console.WriteLine("task 运行结束");
            }, source.Token).ContinueWith(task => Console.WriteLine(task.Status));
            await Task.Delay(TimeSpan.FromSeconds(3));
            source.Cancel();
            Console.WriteLine("取消任务");
        }

结果是RanToCompletion而不是Canceled,这说明任务根本没有被取消

我们如果不在取消前等待3秒,并且添加任务运行开始日志

代码语言:javascript
复制
        public static async void Foo()
        {
            var source = new CancellationTokenSource();

            Task.Run(() =>
            {
                Console.WriteLine("task 运行开始");
                Thread.Sleep(TimeSpan.FromSeconds(5));
                Console.WriteLine("task 运行结束");
            }, source.Token).ContinueWith(task => Console.WriteLine(task.Status));
            source.Cancel();
            Console.WriteLine("取消任务");
        }

任务有被正常取消了

原因

实际上,当Task.Run的任务真正开始执行后,调用CancellationSource.Cancel方法并不能取消任务,或者结束调用线程。调用的方法仍然会顺利执行。

那么带有CancellationToken的方法重载有什么用呢?

1、如第三个例子所示,在任务运行开始之前,调用Cancel可以直接取消任务,避免额外消耗一个线程

2、当对应的token,在执行体中抛出OperationCanceledException,(即调用CancellationToken.ThrowIfCancellationRequested())可以将其捕获,并且将任务状态置为Canceled

代码语言:javascript
复制
        public static async void Foo()
        {
            var source = new CancellationTokenSource();

            Task.Run(() =>
            {
                Console.WriteLine("task 运行开始");
                Thread.Sleep(TimeSpan.FromSeconds(5));
                source.Token.ThrowIfCancellationRequested();
                Console.WriteLine("task 运行结束");
            },source.Token).ContinueWith(task => Console.WriteLine(task.Status));
            await Task.Delay(TimeSpan.FromSeconds(3));
            source.Cancel();
            Console.WriteLine("取消任务");
        }

而如果没有使用这个重载,任务会因未捕获异常而终止,并非取消

代码语言:javascript
复制
        public static async void Foo()
        {
            var source = new CancellationTokenSource();

            Task.Run(() =>
            {
                Console.WriteLine("task 运行开始");
                Thread.Sleep(TimeSpan.FromSeconds(5));
                source.Token.ThrowIfCancellationRequested();
                Console.WriteLine("task 运行结束");
            }).ContinueWith(task => Console.WriteLine(task.Status));
            await Task.Delay(TimeSpan.FromSeconds(3));
            source.Cancel();
            Console.WriteLine("取消任务");
        }

所以我们为Task.Run添加了CancellationToken后仍需要,在方法执行时手动判断token是否取消

参考链接:


本文会经常更新,请阅读原文: https://xinyuehtx.github.io/post/Task%E7%9C%9F%E7%9A%84%E5%8F%96%E6%B6%88%E4%BA%86%E4%B9%88.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名黄腾霄(包含链接: https://xinyuehtx.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-01-24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 原因
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档