C#多线程(4)——任务并行库TPL

文章目录

      • 1 什么是TPL
      • 2 创建与启动任务
      • 3 等待任务
      • 4 任务中的异常处理
      • 5 取消任务

1 什么是TPL

T P L \textcolor{red}{TPL} TPL(Task Parallel Library)任务并行库,是从.NetFramwork4.0后引入的基于异步操作的一组API。TPL的底层是基于多线程实现的,但是它相较于直接使用多线程,更为简单,它向程序员隐藏了与线程池交互的底层代码。在.NetFramwork4.0后,微软更推荐程序员使用TPL去编写多线程代码或者并行代码。
TPL的核心是任务,一个任务代表了一个异步操作,该操作可以使用或不适用独立的线程运行。
一个任务可以和其他的任务组合起来,比如同时启动多个任务,等待所有的任务完成;对之前所有任务的结果进行计算,TPL的优势在于具有组合任务API,而不用单独书写线程同步的代码(关注于锁、线程间的信号)。同样在多线程中关于多线程中异常的传播与处理是极为复杂的,而在TPL中,可以通过 A g g r e g a t e E x c e p t i o n \textcolor{red}{AggregateException} AggregateException,捕获底层任务的所有异常,并允许单独处理这些异常。

2 创建与启动任务

创建、启动任务使用到的关键类如下:类定义在 S y s t e m . T h r e a d i n g . T a s k s \textcolor{red}{System.Threading.Tasks} System.Threading.Tasks 命名空间中
在这里插入图片描述

       public static void Main(string[] args)
        {
            void TaskMethod(string name) {
                Console.WriteLine("Task{0} is runing on ThreadId {1} Is ThreadPool Thread {2}",
                    name,
                    Thread.CurrentThread.ManagedThreadId,
                    Thread.CurrentThread.IsThreadPoolThread);
            }


            int  TaskMethod_1(string name) {

                Console.WriteLine("Task{0} is runing on ThreadId {1} Is ThreadPool Thread {2}",
                     name,
                     Thread.CurrentThread.ManagedThreadId,
                     Thread.CurrentThread.IsThreadPoolThread);

                return 0;
            }


            #region 1 创建任务
            TaskMethod("Main");
            //public Task(Action action) 传入一个无返回的委托函数
            Task t1 = new Task(() => TaskMethod("t1"));
            Task t2 = new Task(() => TaskMethod("t2"));
            t1.Start();//显示创建Task需要运行start方法才能运行任务中的方法
            t2.RunSynchronously(); //RunSynchronously()在当前线程上运行任务方法
            Task.Run(() => TaskMethod("t3"));
            Task.Factory.StartNew(() => TaskMethod("t4"));


            //Task<T> 管理有返回值的工作单元  public Task(Func<TResult> function)
            //通过传入有返回的委托函数来创建任务对象
            Task<int> t5 = new Task<int>(() => TaskMethod_1("t5"));
            t5.Start();
            Console.WriteLine(t5.Result); //获得任务结果

            Console.ReadKey();
            #endregion

        }

在这里插入图片描述

3 等待任务

有以下方式可以等待任务完成:

  • 调用Wait方法(可选择指定超时时间)
  • 访问Result属性(当使用Task时)
  • Task.WaitAll(等待所有指定任务完成)
  • Task.WaitAny(等待任意一个任务完成)。
            #region 2 等待任务
         
            Console.OutputEncoding = Encoding.Unicode;
            Console.WriteLine("起始执行时间为{0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));

            List<Task> list = new List<Task>();
            
            for (int i = 1; i <= 5; i++)
            {
                int tempI = i; //需要使用局部变量,因为for循环在数据量很小的时候,for循环结束时 task启动了,但是可能还未执行。由于共享变量i,所有在真正执行Task时,线程名称将一样 i=11
                Task t = new Task(()=>TaskMethod_2("-"+ tempI, tempI));
                list.Add(t);
                t.Start();//因为多线程的启动并不意味着立马进行,需要等待操作系统的调度。
            }

            Task.WaitAll(list.ToArray());//等待所有的线程完成
            Task.WaitAny(list.ToArray());//等待任意一个线程完成后执行,相当于在一个ManualResetEventSlim上等待,

            Console.WriteLine("等待所有任务线程执行完成,结束执行时间为{0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));


            Task<int> t_1 = new Task<int>(() => TaskMethod_2("t_1", 10));
            t_1.Start();
            t_1.Wait();//主线程等待t_1线程完成任务方法。 Wait(TimeSpan timeout) 也可以等待具体的时间
            Console.WriteLine(t_1.Result);

            Console.WriteLine("结束执行时间为{0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            Console.ReadKey();
            #endregion

TaskMethod_2 方法如示例一代码块。方法运行后如下

在这里插入图片描述

4 任务中的异常处理


            #region 3 任务的异常处理
            Task t1 = Task.Factory.StartNew(() => {
                throw new Exception("Task Failed ! ");
            });
            try
            {
                t1.Wait();//当你等待一个任务结束时(通过调用Wait方法或访问其Result属性),所有未处理的异常都会用一个AggregateException对象封装,方便重新抛给调用方。
            }
            catch (AggregateException aex)
            {
                Console.WriteLine(aex.InnerException.Message);  // Task Failed !
            }

            //定义一个父子任务,在父任务与子任务中分别抛出异常
            TaskCreationOptions atp = TaskCreationOptions.AttachedToParent;
            var parent = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("I am Parent");

                Task.Factory.StartNew(() =>   // 子
                {
                    Console.WriteLine("I am Child");
                    throw new Exception("Child Exception");
                }, atp);

                throw new Exception("Parent Exception");

            });

            try {
                parent.Wait();
            }
            catch (AggregateException aex)
            {
                Console.WriteLine(aex.InnerExceptions.Count);  //2 捕获了父异常和子异常
                //这是对于有父子关系的任务,在父任务上等待也会隐式的等待子任务,所有子任务的
                //异常也会传递出来。
            }

            Console.ReadKey();
            #endregion

main 方法块如上,运行结果如下图所示
在这里插入图片描述

当某个任务抛出一个或多个异常时,异常包装在 A g g r e g a t e E x c e p t i o n \textcolor{blue}{AggregateException } AggregateException 异常中。 该异常会传播回与任务联接的线程。 通常,该线程是 等待任务完成 \textcolor{red}{等待任务完成} 等待任务完成的线程
(1)Wait方法
(2)WaitAny方法
(3)WaitAll方法
或访问 Result 属性的线程。AggregateException 通常包含关联任务线程中的所有异常,我们可以在外部通过try catch的方式去处理它,但是这并不意味着并不需要单独处理任务线程的异常,否则可能会因为无解的异常导致程式的中断。

5 取消任务

任务的取消需要用到 C a n c e l l a t i o n T o k e n S o u r c e \textcolor{red}{CancellationTokenSource} CancellationTokenSource C a n c e l l a t i o n T o k e n \textcolor{red}{CancellationToken} CancellationToken类。在取消任务的过程中需要了解一下几点:

  • 可以在构造中传入CancellationToken来构建Task任务,并且CancellationToken可以绑定到多个任务上
  • Task的创建和执行都是独立的,如果在任务执行前取消了任务,那么任务代码将不会执行。如果尝试调用start方法,将会抛出异常InvalidOperationException
  • 任务执行后去执行CancellationTokenSource.Cancel方法,任务不会被取消
  • 需要在任务代码中 显示定义任务中断的逻辑 \textcolor{blue}{显示定义任务中断的逻辑} 显示定义任务中断的逻辑

Main 方法代码块如下


            #region 5 取消任务
            
            Console.OutputEncoding = System.Text.Encoding.UTF8;
            void RunTask(string name) {
                for (int i = 0; i < 10; i++) {
                    Console.WriteLine(name+"运行开始:" + i);
                    Thread.Sleep(1000);
                }
            }

            void RunTaskWithCancellationToken(string name,CancellationToken token) {
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine(name + "运行开始:" + i);
                    Thread.Sleep(1000);
                    token.ThrowIfCancellationRequested(); //在运行的Task任务中,显示定义任务取消的逻辑
                }
            }

            CancellationTokenSource source = new CancellationTokenSource();
            CancellationToken cancellation = source.Token;

            //创建一个和CancellationToken关联的任务类
            //t1任务不支持显示取消任务
            Task t1 = Task.Factory.StartNew(() => RunTask("t1"), cancellation);
            //t2任务支持显示取消任务,在任务执行的逻辑中加了ThrowIfCancellationRequested,标识希望在任务中断是抛出异常
            Task t2 = Task.Factory.StartNew(() => RunTaskWithCancellationToken("t2",cancellation), cancellation);
            //t3任务不支持显示取消任务,且需要手动start去运行任务代码。
            Task t3 = new Task(()=>RunTask("t1"),cancellation);
            
            Thread.Sleep(TimeSpan.FromSeconds(3));

            source.Cancel();

            try
            {
                //在一个任务调度前,取消任务,那么将会抛出System.InvalidOperationException 标识在已经完成的工作上呼叫start动作。
                t3.Start();
            }
            catch (Exception e)
            {

                Console.WriteLine("异常类型{0} ,异常消息{1}",e.GetType().Name,e.Message);
            }


            Console.WriteLine("t1 IsCanceled  {0} ,t1 IsCompleted{1}, t1 IsFaulted{2}, t1 status{3}", t1.IsCanceled, t1.IsCompleted, t1.IsFaulted, t1.Status.ToString());
            Console.WriteLine("t2 IsCanceled  {0} ,t2 IsCompleted{1}, t2 IsFaulted{2}, t2 status{3}", t2.IsCanceled, t2.IsCompleted, t2.IsFaulted, t2.Status.ToString());
            Console.WriteLine("t3 IsCanceled  {0} ,t3 IsCompleted{1}, t3 IsFaulted{2}, t3 status{3}", t3.IsCanceled, t3.IsCompleted, t3.IsFaulted, t3.Status.ToString());
            Console.ReadKey();
            #endregion

运行方法后如下
在这里插入图片描述
当主线程不休眠3s (注释掉 Thread.Sleep(TimeSpan.FromSeconds(3)))后,验证结论一
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言第三十六弹---文件操作(中)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 文件操作 1、文件的顺序读写 1.1、顺序读写函数介绍 1.1.1、fgetc 与 fputc 1.1.2、fgets 与 fputs 1.1.3、fscanf 与 fprintf 1.1.4、fread 与 fwrite 1.…

基于状态机的按键消抖实现

摸鱼记录 Day_14 !(^O^)y review 在day_13中以按键状态判断为例学习了状态分析基于状态机的按键消抖原理-CSDN博客 分析得到了下图&#xff1a; 今日任务&#xff1a;完成此过程 !(^O^)y 小梅哥对应视频&#xff1a; 15B 基于状态机的按键消抖Verilog实现_哔哩哔哩…

《乱弹篇(23)读书摘录》

之前逾月&#xff0c;偷得闲遐&#xff0c;在腾迅视频看了两部以宋朝历史为背景的电视剧《后宫》和《清平乐》&#xff0c;觉得好看并觉得有所裨益&#xff0c;但之后就再也难以找到能让人有耐心去看完的一部电视剧了。在一阵儿唏嘘感叹后&#xff0c;便去读书网搜索小说书读&a…

砝码称重 蓝桥杯

在C中&#xff0c;fabs()和abs()都用于计算数字的绝对值&#xff0c;但它们之间有一些区别。 fabs(double x)&#xff1a;计算浮点数x的绝对值&#xff0c;返回一个double类型的结果。 abs(int x)&#xff1a;计算整数x的绝对值&#xff0c;返回一个int类型的结果。 数组的默…

PolarDB for PostgreSQL-概述

阿里云数据库的概述 本篇罗列了一些知识点和结构。 日志 2. 同步复制&#xff1a;下降20% 异步复制&#xff1a;数据丢失风险&#xff0c; 部署 1.示例&#xff1a; vim polarx.toml 1.测试主库和备库数据一致性 备库是否一致性读 一个节点荡掉&#xff0c;提供服务。 GMS CN…

HCIP —— BGP --- 路由反射器和联邦

目录 IBGP的水平分割 解决办法 路由反射器 --- RR 反射器的环路问题如何解决&#xff1f;&#xff1f;&#xff1f; 1.Originator_ID 起源者ID 2.Cluster_List --- 簇列表 RR的配置 联邦的配置 1.联邦和其他AS设备建EBGP对等体 2.联邦内部IBGP建立对等体 3.联邦内部E…

数据管理【总结】

大数据&#xff1a;是指极其庞大的数据集&#xff0c;通常具备五大特征&#xff08;5个V&#xff09;&#xff1a;数据量大&#xff08;Volume&#xff09;、数据类型多样&#xff08;Variety&#xff09;、数据生成速度快&#xff08;Velocity&#xff09;、数据真实性高&…

最新Claude3注册教程:解决Claude3 Opus被封无法发送手机验证码问题

Claude3注册指南&#xff1a;解决无法发送手机验证码问题 Anthropic在3月4日宣布推出最新的大型语言模型&#xff08;LLM&#xff09;系列——Claude 3。这一系列模型在多种认知任务上树立了新的性能标准&#xff0c;包括Claude 3 Haiku、Claude 3 Sonnet 和 Claude 3 Opus三个…

2024年纯前端VUE在线编辑微软Office/金山WPS的Word文档,支持私有化部署!

现在&#xff0c;随着数字化进程渗透到到各行各业&#xff0c;数据安全已经成为了数字化革命中的重要组成部分&#xff0c;而在线Office成在OA、ERP、文档系统中得到了广泛的应用&#xff0c;为我国的信息化事业也做出了巨大贡献。随着操作系统、浏览器及Office软件的不断升级和…

node的安装与介绍

安装 下载地址 node官网首页就会有两个安装选择&#xff0c;会根据当前电脑的系统自动显示对应的安装包&#xff0c;一个长期维护版&#xff08;LTS&#xff09;,一个是尝鲜版&#xff0c;记住选择LTS版本 安装指定版本下载截图 安装过程截图&#xff08;非常简单&#xff…

短剧小程序:掌中剧院,随时演绎精彩

在快节奏的现代生活中&#xff0c;人们越来越追求高效与便捷。为了满足广大用户对短剧内容的热爱和追求&#xff0c;我们推出了全新的短剧小程序&#xff0c;让精彩剧情触手可及&#xff0c;随时随地为您带来欢乐与感动。 一、轻松点播&#xff0c;随享短剧魅力 通过短剧小程…

Talk|加州大学圣地亚哥分校程旭欣:视觉反馈下足式机器人的全身操作与运动

本期为TechBeat人工智能社区第576期线上Talk。 北京时间3月6日(周三)20:00&#xff0c;加州大学圣地亚哥分校博士生—程旭欣的Talk已准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “视觉反馈下足式机器人的全身操作与运动”&#xff0c;向大家系统地介绍…

【开源】JAVA+Vue.js实现大学生相亲网站

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 查询会员4.2 查询相亲大会4.3 新增留言4.4 查询新闻4.5 新增新闻 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的大学生相亲网站&#xff0c;包含了会员管理模块、新闻管…

Vue开发实例(十)Tabs标签页打开、关闭与路由之间的关系

创建标签页 一、创建标签页二、点击菜单展示新标签页1、将标签数据作为全局使用2、菜单点击增加标签页3、处理重复标签4、关闭标签页 三、点击标签页操作问题1&#xff1a;点击标签页选中菜单进行高亮展示问题2&#xff1a;点击标签页路由也要跳转 四、解决bug 先展示最终效果 …

项目管理工具进度猫:自我管理的应用

在飞速发展的现代社会中&#xff0c;每个人都面临着巨大的竞争压力&#xff0c;如何在这激烈的环境中脱颖而出&#xff0c;实现个人的成长与成功&#xff1f;答案就在我们的日常行为中——自我管理。 一、自我管理的定义 自我管理&#xff0c;简单来说&#xff0c;就是对自己…

mysql8安装配置(最新版)

目录 一、下载mysql8 二、安装mysql8 三、配置mysql 一、下载mysql8 下载链接&#xff1a;https://pan.quark.cn/s/58d9072e51c4 二、安装mysql8 双击msi文件 选择custom 根据所需选择组件 修改安装路径 选中execute&#xff0c;安装&#xff0c;弹出提示安装VS的提示框之后…

LT6813/ADBMS1818底层驱动---均衡控制

1、LT6813采用内部均衡的原理 2、平衡控制结构体 根据数据库中读取的控制值设置平衡。要为单元设置平衡&#xff0c;必须将相应的位写入配置寄存器中。LTC 驱动程序仅执行数据库中 BMS 写入的数据。 参数 ltc_stateLTC 状态机的状态pSpi接口指向 SPI 配置的指针pTxBuff &…

Android SDK2 (实操三个小目标)

书接上回&#xff1a;Android SDK 1&#xff08;概览&#xff09;-CSDN博客 今天讲讲三个实际练手内容&#xff0c;用的是瑞星微的sdk。 1 实操编译Android.bp 首先还是感叹下&#xff0c;现在的系统真的越搞越复杂&#xff0c;最早只有gcc&#xff0c;后面多了make&#xf…

2.3_9 读者-写者问题

2.3_9 读者-写者问题 &#xff08;一&#xff09;问题描述 有读者和写者两组并发进程&#xff0c;共享一个文件&#xff0c;当两个或两个以上的读进程同时访问共享数据时不会产生副作用&#xff0c;但若某个写进程和其他进程&#xff08;读进程或写进程&#xff09;同时访问共…

【nvm】nvm的安装和使用

简言 nvm(nvm-windows)的安装和使用。 nvm 允许你通过命令行快速安装和使用不同版本的 node。 nvm 适用于任何符合 POSIX 标准的 shell&#xff08;sh、dash、ksh、zsh、bash&#xff09;&#xff0c;尤其适用于以下平台&#xff1a;Unix、macOS 和 windows WSL。 不过 nvm 在…