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=11Task 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)))后,验证结论一
在这里插入图片描述

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

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

相关文章

App自动化测试笔记(八):pytest运行方式和配置文件

1、安装 pip3 install pytest 显示当前版本pytest --version 2、pytest运行方式 测试代码&#xff1a; class TestLogin():def setup(self):print("this is setUp")def teardown(self):print("this is tearDown")def test_Login(self):print("start…

Python - Pycharm 配置 autopep8 并设置快捷键

什么是 PEP8 官方&#xff1a;PEP 8 – Style Guide for Python Code | peps.python.org PEP8 是 Python 官方推出的一套编码的规范&#xff0c;只要代码不符合它的规范&#xff0c;就会有相应的提示&#xff0c;还可以让代码自动的格式化 Pycharm 自带的代码格式化 ​ 但这…

有一点好看的wordpress外贸独立站模板

手机配件wordpress外贸网站模板 充电器、移动电源、手机膜、手机电池、手机壳、手机转接头等手机配件wordpress外贸网站模板。 https://www.jianzhanpress.com/?p3809 车载电器wordpress外贸网站模板 车载吸尘器、空气净化器、行车记录仪、车载充电器、车载影音导航等车载电…

2.26-3.6

2.26 下面是项目vue脚手架 下面是node环境文件夹 2.27 npm config get prefix npm config set prefix "D:\software\nodejs"得到下面 创建脚手架 npm i vue/cli -g在项目脚手架里 vue create vue-project-1where npx vue使用vue cli创建前端工程 https://reg…

HTML入门:简单了解 HTML 和浏览器

你好&#xff0c;我是云桃桃。今天来简单了解一下 HTML 以及浏览器。 HTML 是什么&#xff1f; HTML&#xff08;全称&#xff1a;Hypertext Markup Language&#xff09;是一种标记语言&#xff0c;用于创建和呈现网页的结构和内容。 它由一系列标签&#xff08;或称为元素…

Error:java:JDK isn‘t specified for module “模块名称“

可能是创建模块后不小心删掉了.idea.或.idea出错 只要删除.idea&#xff0c;close project出去&#xff0c;重新进让idea自动下载

Python3 字符串

字符串是 Python 中最常用的数据类型。我们可以使用引号( 或 " )来创建字符串。 创建字符串很简单&#xff0c;只要为变量分配一个值即可。例如&#xff1a; var1 Hello World! var2 "Runoob" Python 访问字符串中的值 Python 不支持单字符类型&#xff…

软件测试必学的16个高频数据库操作及命令

数据库作为软件系统数据的主要存取与操作中心&#xff0c;广泛应用于企业当中。在企业中常用的数据库管理系统有 ORACLE、MS SQL SERVER、MySQL等。其中以免费的 MySQL 最多&#xff0c;特别在中小型互联网公司里。 因此&#xff0c;本文的数据库操作是基于 MySQL 数据库系统下…

【文献计量】安装endnote注意事项

1.前言 EndNote 是一款广受学者、研究人员、学生和图书管理员等使用的参考管理软件。它由 Clarivate Analytics 开发&#xff0c;用于管理文献引用和编排参考文献列表。EndNote 可以帮助用户在撰写科研论文、书籍或任何学术出版物时&#xff0c;高效地组织、管理和引用研究资料…

网络原理初识

一、IP地址 概念 IP 地址主要用于标识网络主机、其他网络设备&#xff08;如路由器&#xff09;的网络地址。简单说&#xff0c; IP 地址用于定位主机 的网络地址 。 就像我们发送快递一样&#xff0c;需要知道对方的收货地址&#xff0c;快递员才能将包裹送到目的地。 二、…

SpringBoot集成ElasticSearch(ES)

ElasticSearch环境搭建 采用docker-compose搭建&#xff0c;具体配置如下&#xff1a; version: 3# 网桥es -> 方便相互通讯 networks:es:services:elasticsearch:image: registry.cn-hangzhou.aliyuncs.com/zhengqing/elasticsearch:7.14.1 # 原镜像elasticsearch:7.…

UML简述(项目立项、设计、需求整理必备)

UML目录 前言1、UML概述1.1、基本概念1.2、UML图类型说明1.3、UML的41视图 2、UML图详细图示2.1、类图2.2、对象图2.3、组件图2.4、部署图2.5、包图2.6、用例图2.7、状态图2.8、活动图2.9、时序图2.10、通信图&#xff08;协作图&#xff09;2.11、定时图&#xff08;计时图&am…