【转】1.9 Asp.Net Core 轻松学-多线程之取消令牌(

目录

  • 前言
  • 1. 多线程请求合并数据源
  • 2. 对长时间阻塞调用的异步取消令牌应用
  • 3. CancellationToken 的链式反应
  • 4. CancellationToken 令牌取消的三种方式
  • 结束语
  • 示例代码下载

 

前言

    取消令牌(CancellationToken) 是 .Net Core 中的一项重要功能,正确并合理的使用 CancellationToken 可以让业务达到简化代码、提升服务性能的效果;当在业务开发中,需要对一些特定的应用场景进行深度干预的时候,CancellationToken 将发挥非常重要的作用。

1. 多线程请求合并数据源

在一个很常见的业务场景中,比如当请求一个文章详细信息的时候,需要同时加载部分点赞用户和评论内容,这里一共有 3 个任务,如果按照常规的先请求文章信息,然后再执行请求点赞和评论,那么我们需要逐一的按顺序去数据库中执行 3 次查询;但是利用 CancellationToken ,我们可以对这 3 个请求同时执行,然后在所有数据源都请求完成的时候,将这些数据进行合并,然后输出到客户端

1.1 合并请求文章信息

       public static void Test(){Random rand = new Random();CancellationTokenSource cts = new CancellationTokenSource();List<Task<Article>> tasks = new List<Task<Article>>();TaskFactory factory = new TaskFactory(cts.Token);foreach (var t in new string[] { "Article", "Post", "Love" }){Console.WriteLine("开始请求");tasks.Add(factory.StartNew(() =>{var article = new Article { Type = t };if (t == "Article"){article.Data.Add("文章已加载");}else{for (int i = 1; i < 5; i++){Thread.Sleep(rand.Next(1000, 2000));Console.WriteLine("load:{0}", t);article.Data.Add($"{t}_{i}");}}return article;}, cts.Token));}Console.WriteLine("开始合并结果");foreach (var task in tasks){Console.WriteLine();var result = task.Result;foreach (var d in result.Data){Console.WriteLine("{0}:{1}", result.Type, d);}task.Dispose();}cts.Cancel();cts.Dispose();Console.WriteLine("\nIsCancellationRequested:{0}", cts.IsCancellationRequested);}

上面的代码定义了一个 Test() 方法,在方法内部,首先定义了一个 CancellationTokenSource 对象,该退出令牌源内部创建了一个取消令牌属性 Token ;接下来,使用 TaskFacory 任务工厂创建了 3 个并行任务,并把这个任务存入 List<Task> 列表对象中,在任务开始后,马上迭代 tasks 列表,通过同步获取每个任务的执行 Result 结果,在取消令牌没有收到取消通知的时候,任务将正常的执行下去,在所有任务都执行完成后,将 3 个请求结果输出到控制台中,同时销毁任务释放线程资源;最后,执行 cts.Cancel()取消令牌并释放资源,最后一句代码将输出令牌的状态。

1.2 执行程序,输出结果

通过上面的输出接口,可以看出,红色部分是模拟请求,这个请求时多线程进行的,Post 和 Love 交替出现,是因为在程序中通过线程休眠的方式模拟网络阻塞过程,蓝色为合并结果部分,可以看到,虽然“文章信息”已经加载完成,但是因为 Post 和 Love 还在请求中,由于取消令牌未收到退出通知,所以合并结果会等待信号,在所有线程都执行完成后,通过 cts.Cancel() 通知令牌取消,所有事件执行完成,控制台打印结果黄色部分为令牌状态,显示为 True ,令牌已取消。

2. 对长时间阻塞调用的异步取消令牌应用

在某些场景中,我们需要请求外部的第三方资源,比如请求天气预报信息;但是,由于网络等原因,可能会造成长时间的等待以致业务超时退出,这种情况可以使用 CancellationToken 来进行优化,但请求超过指定时长后退出,而不必针对每个 HttpClient 进行单独的超时设置

2.1 获取天气预报

        public async static Task GetToday(){CancellationTokenSource cts = new CancellationTokenSource();cts.CancelAfter(3000);HttpClient client = new HttpClient();var res = await client.GetAsync("http://www.weather.com.cn/data/sk/101110101.html", cts.Token);var result = await res.Content.ReadAsStringAsync();Console.WriteLine(result);cts.Dispose();client.Dispose();}

在上面的代码中,首先定义了一个 CancellationTokenSource 对象,然后马上发起了一个 HttpClient 的 GetAsync 请求(注意,这种使用 HttpClient 的方式是不正确的,详见我的博客 HttpClient的演进和避坑 ;在 GetAsync 请求中传入了一个取消令牌,然后立即发起了退出请求 Console.WriteLine(result); 不管 3 秒后请求是否返回,都将取消令牌等待信号,最后输出结果释放资源

  • 注意:如果是因为取消令牌退出引起请求中断,将会抛出任务取消的异常 TaskCanceledException
  • 执行程序输出结果

3. CancellationToken 的链式反应

可以使用创建一组令牌,通过链接各个令牌,使其建立通知关联,当 CancellationToken 链中的某个令牌收到取消通知的时候,由链式中创建出来的 CancellationToken 令牌也将同时取消

3.1 创建链式测试代码

public async static Task Test(){CancellationTokenSource cts1 = new CancellationTokenSource();CancellationTokenSource cts2 = new CancellationTokenSource();var cts3 = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token);cts1.Token.Register(() =>{Console.WriteLine("cts1 Canceling");});cts2.Token.Register(() =>{Console.WriteLine("cts2 Canceling");});cts2.CancelAfter(1000);cts3.Token.Register(() =>{Console.WriteLine("root Canceling");});var res = await new HttpClient().GetAsync("http://www.weather.com.cn/data/sk/101110101.html", cts1.Token);var result = await res.Content.ReadAsStringAsync();Console.WriteLine("cts1:{0}", result);var res2 = await new HttpClient().GetAsync("http://www.weather.com.cn/data/sk/101110101.html", cts2.Token);var result2 = await res2.Content.ReadAsStringAsync();Console.WriteLine("cts2:{0}", result2);var res3 = await new HttpClient().GetAsync("http://www.weather.com.cn/data/sk/101110101.html", cts3.Token);var result3 = await res2.Content.ReadAsStringAsync();Console.WriteLine("cts3:{0}", result3);}

上面的代码定义了 3 个 CancellationTokenSource ,分别是 cts1,cts2,cts3,每个 CancellationTokenSource 分别注册了 Register 取消回调委托,然后,使用 HttpClient 发起 3 组网络请求;其中,设置 cts2 在请求开始 1秒 后退出,预期结果为:当 cts2 退出后,由于 cts3 是使用 CreateLinkedTokenSource(cts1.Token, cts2.Token) 创建出来的,所以 cts3 应该也会被取消,实际上,无论 cts1/cts2 哪个令牌取消,cts3 都会被取消

3.2 执行程序,输出结果

从上图可以看到,红色部分输出结果是:首先 cts2 取消,接着产生了链式反应导致 cts3 也跟着取消,蓝色部分为 cts1 的正常请求结果,最后输出了任务退出的异常信息

4. CancellationToken 令牌取消的三种方式

CancellationToken 定义了三种不同的取消方法,分别是 Cancel(),CancelAfter(),Dispose();这三种方式都代表了不同的行为方式

4.1 演示取消动作

        public static void Test(){CancellationTokenSource cts1 = new CancellationTokenSource();cts1.Token.Register(() =>{Console.WriteLine("\ncts1 ThreadId: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);});cts1.Cancel();Console.WriteLine("cts1 State:{0}", cts1.IsCancellationRequested);CancellationTokenSource cts2 = new CancellationTokenSource();cts2.Token.Register(() =>{Console.WriteLine("\ncts2 ThreadId: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);});cts2.CancelAfter(500);System.Threading.Thread.Sleep(1000);Console.WriteLine("cts2 State:{0}", cts2.IsCancellationRequested);CancellationTokenSource cts3 = new CancellationTokenSource();cts3.Token.Register(() =>{Console.WriteLine("\ncts3 ThreadId: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);});cts3.Dispose();Console.WriteLine("\ncts3 State:{0}", cts3.IsCancellationRequested);}

4.2 执行程序,输出结果如下

  上面的代码定义了 3 个 CancellationTokenSource,分别是 cts1/cts2/cts3;分别执行了 3 中不同的取消令牌的方式,并在取消回调委托中输出线程ID,从输出接口中看出,当程序执行 cts1.Cancel() 方法后,取消令牌立即执行了回调委托,并输出线程ID为:1;cts2.CancelAfter(500) 表示 500ms 后取消,为了获得令牌状态,这里使线程休眠了 1000ms,而 cts3 则直接调用了 Dispose() 方法,从输出结果看出,cts1 运行在和 Main 方法在同一个线程上,线程 ID 都为 1,而 cts2 由于使用了延迟取消,导致其在内部新创建了一个线程,其线程 ID 为 4;最后,cts3由于直接调用了 Dispose() 方法,但是其 IsCancellationRequested 的值为 False,表示未取消,而输出结果也表明,没有执行回调委托

结束语

  • 通过本文,我们学习到了如何在不同的应用场景下使用 CancellationToken
  • 掌握了合并请求、中断请求、链式反应 三种使用方式
  • 最后还了解到三种不同的取消令牌方式,知道了各种不同取消方式的区别

示例代码下载

https://github.com/lianggx/EasyAspNetCoreDemo/tree/master/Ron.ThreadingDemo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/437296.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python怎么改背景_python IDE背景怎么改

首先&#xff0c;在已经下载好的python文件目录下&#xff0c;找到config-highlight.def文件&#xff0c;我的是在H:\python\python3**\Lib\idlelib**文件夹下。 打开文件后&#xff0c;你会看到一些默认的颜色配置&#xff0c;比如经典的颜色配置就是白色背景&#xff0c;一般…

QML程序发布时无法正常运行的解决办法

1、运行依赖 以我的一个项目为例&#xff0c;此程序使用QQuickWidget将QWidget和QML结合。程序debug版发布时依赖的库如下&#xff1a; 大部分dll可以在Visual Studio中调试时的控制台中看出已加载的dll&#xff0c;只需到Qt安装目录下找到对应的dll即可。但是某些dll并没有提…

【转】UML基础: 第1部分 - 类图 (Class Diagram)

类图 类图是一个静态图。它代表了应用程序的静态视图。类图不仅用于可视化&#xff0c;描述和记录系统的不同方面&#xff0c;还用于构建软件应用程序的可执行代码。 类图描述了一个类的属性和操作&#xff0c;以及对系统施加的约束。类图被广泛用于面向对象系统的建模&#…

int转换为cstring_PostgreSQL 隐式类型转换探秘

个人简介何小栋&#xff0c; 从事产品研发和架构设计工作&#xff0c;对Oracle、PostgreSQL有深入研究&#xff0c;ITPUB数据库版块资深版主。现就职于广州云图数据技术有限公司&#xff0c;系统架构师&#xff0c;博客&#xff1a;http://blog.itpub.net/6906/摘要本文通过与O…

【转】UML基础: 第 2 部分 - 对象图 (Object Diagram)

对象图是从类图派生的&#xff0c;因此对象图依赖于类图。 对象图表示类图的一个实例。类图和对象图的基本概念是相似的。对象图也表示系统的静态视图&#xff0c;但这个静态视图是系统在特定时刻的快照。 对象图用于呈现一组对象及其关系作为实例。 对象图的目的 图表的目…

外部依赖项很多未定义标识符_从日本编程书籍《我的第一本编程书》中译版看中文例程如何扬长避短——标识符(一)

日本作者平山尚在前言归结了本书的三点独特之处&#xff1a;从始至终只编写一个程序&#xff08;俄罗斯方块游戏&#xff09;使用专门的工具绝对面向首次接触程序的人群第一点&#xff0c;优势是一个项目主体贯穿全书&#xff0c;但同时很考验编排顺序&#xff0c;以及技术覆盖…

Qt: QTableView如何获取(行)选中、行切换信息

**情景&#xff1a;**做一个信息表格&#xff0c;需要多个Model切换&#xff0c;必须用QTableView&#xff0c;而不能用QTableWidget&#xff0c;因为后者不可以进行setModel()。 方案&#xff1a; QTableView和选择有关的的信号有&#xff1a; void activated(const QModelI…

动态网站的技术路线_3个好玩实用小网站!闲暇时间不妨打开看看

感谢你关注“最佳应用”每篇文章解决某行业或某人群的一个痛点第八十四期原创文章By&#xff1a;小佳昨天刷抖音听了一首很有魔性的歌曲&#xff0c;结果分享到社交平台&#xff0c;没想到被很多键盘侠喷了&#xff0c;留言全是批判“审美有毒”&#xff0c;这种垃圾歌曲能火就…

【转】1.DThread、ThreadPool、Task、Parallel的基本用法、区别以及弊端

多线程的操作在程序中也是比较常见的&#xff0c;比如开启一个线程执行一些比较耗时的操作(IO操作)&#xff0c;而主线程继续执行当前操作&#xff0c;不会造成主线程阻塞。线程又分为前台线程和后台线程&#xff0c;区别是&#xff1a;整个程序必须要运行完前台线程才会退出&a…

vue传值到后端_Vue.js快速入门就从这儿开始特别是后端程序员

自从前后端分离开始变成主流后&#xff0c;曾经的Jsp、FreeMarker、Velocity、Thymeleaf貌似慢慢被遗忘了&#xff0c;取而代之的是兴起的前端主流语言&#xff0c;比如Vue、React和AngularJS介绍VueVue其实是借鉴了 Angular&#xff0c;目前GitHubstar数最多&#xff0c;建议后…

unity3d collider自动调整大小_自动网格组合建模工具Unity游戏素材资源

分享最新的CG教程与素材资讯&#xff01;人人素材RRCG-专业的CG艺术交流网站点击上方蓝字关注人人素材本游戏资料是自动网格组合建模工具Unity游戏素材资源&#xff0c;大小&#xff1a;735 KB &#xff0c;格式&#xff1a;unitypackage&#xff0c;使用软件&#xff1a;unity…

【转】2.2[译]async/await中阻塞死锁

这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题&#xff0c;以及如何避免出现这种死锁。内容主要是从作者Stephen Cleary的两篇博文中翻译过来. 原文1&#xff1a;DontBlock on Async Code 原文2&#xff1a;why the AspNetSynchronizationContext was remove…

Java运用自身排序算法将数组或容器进行随机打乱。

基本思路&#xff1a;数组调用Arrays.sort(T[] a,Comparator<? super T> c),对Comparator进行重写。运用Random类 &#xff0c;实现对数字的随机排序。 对数字进行随机排序。代码如下&#xff1a; import java.util.Arrays; import java.util.Comparator; import java…

文本编码解释

一张图解释字符集 举例说明什么是编码&#xff1a; UTF-8编码 等长编码对于英文来说浪费空间&#xff0c;所以出现了变长编码UTF系列&#xff0c;如UTF8&#xff0c;UTF16&#xff0c;UTF32。 UTF8的编码对象是整个Unicode字符集&#xff0c;所以可以表示所有国家的语言而不会…

tkinter label_tkinter做一个简易提词板

我们看综艺的时候&#xff0c;经常能看到现场的提词板。今天我们就用tkinter做一个简单的提词板&#xff0c;用到了tkinter的label来动态显示文字。我们就以最近火爆的《想见你》这首歌为例&#xff0c;按照歌词时间来显示歌词。首先我们要准备好歌词文件&#xff0c;一般是lrc…

web前后端 http转https

1.转换前准备 http转https需要一个证书、本文已ssl证书举例&#xff0c;只有认证的证书才能被认可。阿里云可以申请免费的证书&#xff0c; 但是生成证书需要域名。且域名要绑定ip。故ssl申请前需要域名。可上阿里云购买。 证书申请教程:证书申请 绑定ip:如果没有服务器、也…

Qt开发技巧:编写.pro文件,在构建流程中加入命令行的方法

1、在项目构建前执行命令 在项目构建前执行命令cmd存在问题system(cmd)存在执行多次的问题&#xff0c;可以参考message函数的QMake Manual说明 优化后的表达式如下&#xff0c;这样此命令只会在构建前执行一次&#xff1a; !build_pass:system(cmd) 2、在链接前后执行&#x…

c++清空输入缓冲区_干货 | C++的输入输出方法

C和C并没有将输入与输出实现在语言中&#xff0c;而是在类库中实现。作为C的超集&#xff0c;C继承了C的输入输出方法。同时将输入与输出视为字流。流充当了程序和流源流目标之间的桥梁。本文将介绍C的输入输出方式&#xff0c;并且浅谈C与C均有的输入输出方式对C有一定学习的同…

Qt应用程序发布:Qt应用程序添加版本版权生产商等信息

设置方法 在QMake Manual手册中搜索关于QMAKE_TARGET内容可以看到有如下QMake变量&#xff1a; QMAKE_TARGET_COMPANY&#xff1a;用于指定生产商QMAKE_TARGET_DESCRIPTION&#xff1a;用于描述应用程序QMAKE_TARGET_COPYRIGHT&#xff1a;用于声明版权QMAKE_TARGET_PRODUCT&…

stl源码剖析_STL源码剖析 阅读笔记(二)allocator

一、空间分配器 allocator从使用上看&#xff0c;空间分配在任何语言的任何组件都不需要我们去过多关心&#xff0c;因为语言、组件的底层肯定都比较完整的做了这件事情。从实现上看&#xff0c;学习 allocator 的原理在源码学习中是首当其冲。因为没有空间分配&#xff0c;则无…