.Net多线程编程—任务Task

1 System.Threading.Tasks.Task简介

一个Task表示一个异步操作,Task的创建和执行是独立的。

只读属性:

返回值

名称

说明

object

AsyncState

表示在创建任务时传递给该任务的状态数据

TaskCreationOptions

CreationOptions

获取用于创建此任务的 TaskCreationOptions

CurrentId

当前正在执行 Task 的 ID

AggregateException

Exception

获取导致 AggregateException 提前结束的 Task。如果 Task 成功完成或尚未引发任何异常,则返回 null

TaskFactory

Factory

提供对用于创建 Task 和 Task<TResult> 的工厂方法的访问

int

Id

获取此 Task 实例的 ID

bool

IsCanceled

指明此 Task 实例是否由于被取消的原因而已完成执行

bool

IsCompleted

指明此 Task 是否已完成

bool

IsFaulted

指明Task 是否由于未经处理异常的原因而完成

TaskStatus

Status

获取此任务的 TaskStatus

2 Task状态和生命周期

一个Task实例只会完成其生命周期一次,当Task达到它的3种可能的最终状态之一时,它就再也回不去之前的状态了。任务的生命周期从TaskStatus.Created状态真正开始。

1) 初始状态

Task实例有三种可能的初始状态

说明

TaskStatus.Created

该任务已初始化,但尚未被计划。使用Task构造函数创建Task实例时的初始状态。

TaskStatus.WaitingForActivation

该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划。一个任务的初始状态,这个任务只有当其依赖的任务完成之后才会被调度。

TaskStatus.WaitingToRun

该任务已被计划执行,但尚未开始执行。使用TaskFactory.StartNew创建的任务的初始状态。

2)中间状态

Task实例有两种可能的中间状态

说明

TaskStatus.Running

该任务正在运行,但尚未完成

TaskStatus.WaitingForChildrenToComplete

该任务已完成执行,正在隐式等待附加的子任务完成

3) 最终状态

Task实例有三种可能的最终状态

说明

TaskStatus.Canceled

该任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 对取消进行了确认,此时该标记处于已发送信号状态;或者在该任务开始执行之前,已向该任务的 CancellationToken 发出了信号。Task属性IsFaulted被设置为true

TaskStatus.Faulted

由于未处理异常的原因而完成的任务。Task属性IsCanceled被设置为true

TaskStatus.RunToCompletion

已成功完成执行的任务。Task属性IsCompleted被设置为true,IsFaulted和IsCanceled被设置为false

3 创建并执行任务

1)public Task StartNew(Action action)

参数:

  action:要异步执行的操作委托

返回值:

  已启动的 System.Threading.Tasks.Task

异常:

  System.ArgumentNullException:当 action 参数为 null 时引发的异常。

2)public static Task Run(Action action)

参数:

  action:表示在线程池执行的队列的任务

返回值:

  已启动的 System.Threading.Tasks.Task

异常:

  System.ArgumentNullException:当 action 参数为 null 时引发的异常。

3)public void Start()

启动 System.Threading.Tasks.Task,并将它安排到当前的 System.Threading.Tasks.TaskScheduler中执行。

异常:

  System.ObjectDisposedException:已释放 System.Threading.Tasks.Task 实例。

  System.InvalidOperationException:System.Threading.Tasks.Task 未处于有效状态,无法启动。 它可能已启动、已执行或已取消,或者可能已经不支持以直接计划的方式创建。

注意:

  仅使用Task的构造器来创建Task的实例并不能启动任务,还要使用Start才能启动任务。

4)Task.Factory.StartNew与Task.Run

Task.Factory.StartNew重载方法提供更多的参数,可以控制如何计划执行任务以及如何向调试器公开计划任务的机制和控制任务的创建和执行的可选行为。

而Task.Run提供的方法则不具有上述控制机制。

4 等待任务完成

1)public void Wait()

等待 System.Threading.Tasks.Task 完成执行过程

异常:

  ObjectDisposedException:Task 对象已被释放。

  AggregateException:System.Threading.Tasks.Task 已取消或在 System.Threading.Tasks.Task 的执行期间引发了异常。如果任务已被取消,System.AggregateException将包含其 System.AggregateException.InnerExceptions 集合中的 System.OperationCanceledException。

2)public static void WaitAll(params Task[] tasks)

参数:

  tasks:要等待的 Task 实例的数组

异常:

  ObjectDisposedException:一个或多个 Task 中的对象 tasks 已被释放。

  ArgumentNullException:tasks 参数为 null或tasks 参数包含 null 元素。

  AggregateException:在至少一个 Task 实例已取消。如果任务已被取消, AggregateException 异常包含 OperationCanceledException 中的异常其   AggregateException.InnerExceptions 集合。或在至少一个执行期间引发了异常 Task 实例。

说明:

  主线程会等待作为参数传入的任务tasks执行结束才会执行下一条语句。

3)public static int WaitAny(params Task[] tasks)

参数:

  tasks:要等待的 Task 实例的数组

异常:

  System.ObjectDisposedException:System.Threading.Tasks.Task 已被释放。

  System.ArgumentNullException:tasks 参数为 null。

  System.ArgumentException:tasks 参数包含 null 元素。

5 取消任务

使用System.Threading.CancellationToken和System.Threading.CancellationTokenSource中断Task的执行。

1)System.Threading.CancellationToken

传播有关应取消操作的通知

属性:

  public bool IsCancellationRequested { get; }

方法:

  public void ThrowIfCancellationRequested();

  如果已请求取消此标记,则引发 System.OperationCanceledException。

异常:

  System.OperationCanceledException:该标记已请求取消。

      System.ObjectDisposedException:关联的System.Threading.CancellationTokenSource已被释放。

2) System.Threading.CancellationTokenSource

通知 System.Threading.CancellationToken,告知其应被取消

属性:

  public CancellationToken Token { get; }:获取与此 System.Threading.CancellationTokenSource 关联的 System.Threading.CancellationToken。

异常:

  System.ObjectDisposedException:已释放标记源。

方法:

  public void Cancel():传达取消请求。

异常:

  System.ObjectDisposedException:此 System.Threading.CancellationTokenSource 已被释放。

  System.AggregateException:聚合异常包含由相关联的 System.Threading.CancellationToken 上已注册的回调引发的所有异常。

6 任务的返回值

1)Task类型

在第1节中已经介绍了Task。

2)Task<TResult>类型

属性

定义

说明

public static TaskFactory<TResult> Factory { get; }

提供对用于创建 System.Threading.Tasks.Task<TResult> 实例的工厂方法的访问。

public TResult Result { get; }

获取此 System.Threading.Tasks.Task<TResult> 的结果值

方法

  public Task ContinueWith(Action<Task<TResult>> continuationAction)

参数:

  continuationAction:在 System.Threading.Tasks.Task<TResult> 完成时要运行的操作。在运行时,委托将作为一个参数传递给完成的任务。

异常:

  System.ObjectDisposedException:System.Threading.Tasks.Task<TResult> 已被释放。

  System.ArgumentNullException:continuationAction 参数为 null。

注意:

  • 该方法的重载方法提供了更多的控制机制。可以传入CancellationToken、TaskContinuationOptions、TaskScheduler参数。
  • 使用Task.Factory.StartNew方法,如果传入的委托无返回值,那么方法执行的返回结果类型其实是Task<TResult>,通过Task<TResult>类型的Result 属性可以查看返回结果。对于串联的多个任务,若后续的任务要使用上一个任务的结果,那么Task.Factory.StartNew返回值类型必须是Task<TResult>或var。
  • 返回值可以是自定义类型。

7 TaskCreationOptions (枚举类型)

用途:控制任务创建与执行的行为。

说明

TaskCreationOptions.None

指定应使用默认行为

TaskCreationOptions.PreferFairness

提示 System.Threading.Tasks.TaskScheduler 以一种尽可 能公平的方式安排任务,这意味着较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运行

TaskCreationOptions.LongRunning

指定某个任务将是运行时间长、粗粒度的操作。 它会向 System.Threading.Tasks.TaskScheduler 提示,过度订阅可能是合理的。

TaskCreationOptions.AttachedToParent

指定将任务附加到任务层次结构中的某个父级

TaskCreationOptions.DenyChildAttach

如果尝试附有子任务到创建的任务,指定 System.InvalidOperationException 将被引发

TaskCreationOptions.HideScheduler

防止环境计划程序被视为已创建任务的当前计划程序。 这意味着像 StartNew 或 ContinueWith 创建任务的执行操作将被视为 System.Threading.Tasks.TaskScheduler.Default当前计划程序

8 任务计划TaskScheduler

功能:扩展任务执行计划,例如自定义任务计划程序来实现性能加速。

属性:

名称

说明

Current

当前正在执行的任务关联的 TaskScheduler

Id

TaskScheduler 的唯一 ID

MaximumConcurrencyLevel

指示此 TaskScheduler 能够支持的最大并发级别

9 串联多个任务

1)public Task ContinueWith(Action<Task> continuationAction);

参数:

  continuationAction:在 System.Threading.Tasks.Task 完成时要运行的操作。 在运行时,委托将作为一个参数传递给完成的任务。

异常:

  System.ObjectDisposedException:创建了 cancellationToken 的 System.Threading.CancellationTokenSource 已经被释放。

  System.ArgumentNullException:continuationAction 参数为 null。

2)public Task ContinueWith(Action<Task> continuationAction, TaskContinuationOptions continuationOptions);

参数:

  continuationAction:根据在 continuationOptions 中指定的条件运行的操作。 在运行时,委托将作为一个参数传递给完成的任务。

  continuationOptions:用于设置计划延续任务的时间以及延续任务的工作方式的选项。

3)TaskContinuationOptions

enum类型,用于设置计划延续任务的时间以及延续任务的工作方式的选项。 这包括条件(如 System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled)和执行选项(如

System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously)。

说明

ContinuationOptions.None

指定应使用默认行为。默认情况下,完成前面的任务之后将安排运行延续任务,而不考虑前面任务的最终 System.Threading.Tasks.TaskStatus。

ContinuationOptions.LongRunning

指定某个任务将是运行时间长、粗粒度的操作。 它会向 System.Threading.Tasks.TaskScheduler 提示,过度订阅可能是合理的。

ContinuationOptions.AttachedToParent

指定将任务附加到任务层次结构中的某个父级。

ContinuationOptions.DenyChildAttach

如果尝试附有子任务到创建的任务,指定 System.InvalidOperationException 将被引发。

ContinuationOptions.HideScheduler

防止环境计划程序被视为已创建任务的当前计划程序。 这意味着像 StartNew 或 ContinueWith 创建任务的执行操作将被视为System.Threading.Tasks.TaskScheduler.Default当前计划程序。

ContinuationOptions.LazyCancellation

在延续取消的情况下,防止延续的完成直到完成先前的任务

ContinuationOptions.NotOnRanToCompletion

指定不应在延续任务前面的任务已完成运行的情况下安排延续任务。 此选项对多任务延续无效

ContinuationOptions.NotOnFaulted

指定不应在延续任务前面的任务引发了未处理异常的情况下安排延续任务。 此选项对多任务延续无效

ContinuationOptions.OnlyOnCanceled

指定只应在延续任务前面的任务已取消的情况下才安排延续任务。 此选项对多任务延续无效

ContinuationOptions.NotOnCanceled

指定不应在延续任务前面的任务已取消的情况下安排延续任务。 此选项对多任务延续无效

ContinuationOptions.OnlyOnFaulted

指定只应在延续任务前面的任务引发了未处理异常的情况下才安排延续任务。 此选项对多任务延续无效

ContinuationOptions.OnlyOnRanToCompletion

指定只应在延续任务前面的任务已完成运行的情况下才安排延续任务。 此选项对多任务延续无效

ContinuationOptions.ExecuteSynchronously

指定应同步执行延续任务。 指定此选项后,延续任务将在导致前面的任务转换为其最终状态的相同线程上运行。 如果在创建延续任务时已经完成前面的任务,则延续任务将在创建此延续任务的线程上运行。只应同步执行运行时间非常短的延续任务

ContinuationOptions.PreferFairness

提示 System.Threading.Tasks.TaskScheduler 以一种尽可能公平的方式安排任务,这意味着较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运行。

注意:

1)可以通过位操作组合使用多个值。

2)使用ContinuationOptions.None意味着不论前面的任务是否被取消,延续任务都会执行。

异常:

  System.ObjectDisposedException:System.Threading.Tasks.Task 已被释放。

  System.ArgumentNullException:continuationAction 参数为 null。

  System.ArgumentOutOfRangeException:continuationOptions 参数为 System.Threading.Tasks.TaskContinuationOptions 指定无效值。

3)子任务(嵌套任务):在父任务的委托中创建的 System.Threading.Tasks.Task 实例。 子任务包括两种:附加的子任务与分离的子任务

  • 分离的子任务是不依赖于其父级而执行。
  • 附加的子任务是使用 TaskCreationOptions.AttachedToParent 选项创建的,依赖于其父任务而执行。 对父任务使用TaskCreationOptions.DenyChildAttach来阻止子任务附加到父任务。
  • 一个任务可以创建任意数量的附加的子任务和分离的子任务,这仅受系统资源限制。
  • 不提倡创建附加的子任务,这样会大大增加程序设计的复杂性。

10 使用模式

1)创建任务

基本形式:

 1 private void CreatTask()
 2 {
 3     //创建并执行任务
 4     Task task = new Task(() =>
 5     {
 6         //具体操作
 7     });
 8     task.Start();
 9 
10     //创建并将任务加入执行计划,使用StartNew
11     Task.Factory.StartNew(() => {
12         //具体操作
13     });
14 
15     //创建并将任务加入执行计划,使用Run
16     Task.Run(() =>
17     {
18         //具体操作
19     });
20 
21     //安排任务
22     Task.Factory.StartNew(() =>
23     {
24         //具体操作
25     },TaskCreationOptions.PreferFairness);
26 }    

创建附加的子任务:

 1 private void CreateTask_Parent()
 2 {
 3     //附加子任务
 4     var taskParent = Task.Factory.StartNew(() =>
 5     {
 6         //操作......
 7         var child = Task.Factory.StartNew(() =>
 8         {
 9             //具体操作
10         }, TaskCreationOptions.AttachedToParent);
11     });
12     taskParent.Wait();
13 
14 
15     //阻止附加子任务
16     var taskParentZ = Task.Factory.StartNew(() =>
17     {
18         //操作......
19         var child = Task.Factory.StartNew(() =>
20         {
21             //即使设置TaskCreationOptions.AttachedToParent也无法将其附加到父任务
22             //具体操作
23         }, TaskCreationOptions.AttachedToParent);
24     }, TaskCreationOptions.DenyChildAttach);
25     taskParentZ .Wait();
26 }    

2)取消任务

 1 public static void CancelFromExternal_Task()
 2 {
 3     CancellationTokenSource cts = new CancellationTokenSource();
 4 
 5     //其他操作...
 6 
 7     //计算condition
 8     bool condition = ...;
 9     if (condition) cts.Cancel();
10     //或使用Operation2_Task(cts);
11     Operation1_Task(cts);
12     //其他操作...
13 
14 }
15 
16     //1 使用IsCancellationRequested属性
17     private static void Operation1_Task(CancellationTokenSource cts)
18     {
19         CancellationToken ct = cts.Token;
20         Task.Factory.StartNew(() => 
21         {
22             //其他操作...
23             //return只对当前子线程有效
24             if (ct.IsCancellationRequested)
25             { return; }
26             //其他操作...
27         },ct);     
28     }
29 
30     //2 使用抛异常的方式
31     private static void Operation2_Task(CancellationTokenSource     cts)
32     {
33         CancellationToken ct = cts.Token; 
34         Task.Factory.StartNew(() =>
35         {
36             //其他操作...
37             ct.ThrowIfCancellationRequested();
38             //其他操作...
39         }, ct);
40     }    

3)等待任务完成

 1 private void WaitFunc()
 2 {
 3     Task task = new Task(() => 
 4     {
 5         //具体操作
 6     });
 7     task.Start();
 8     task.Wait();
 9 }
10 
11 private void WaitAllFunc()
12 {
13     Task task1 = Task.Run(() =>
14     {
15         //具体操作 
16     });
17     Task task2 = Task.Run(() =>
18     {
19         //具体操作 
20     });
21     //等待task1与task2,直到它们完成为止
22     Task.WaitAll(task1, task2);
23 
24     //等待task1与task2,如果超过1000毫秒则返回。
25     Task.WaitAll(new Task[] { task1, task2 },1000);
26 }

4)串联多个任务

 1 private void contactTasks()
 2 {
 3     var t1 = Task.Factory.StartNew(() =>
 4     {
 5         //具体操作1
 6         //return 返回值;
 7     });
 8 
 9     var t2 = t1.ContinueWith((t) =>
10     {
11         //具体操作2
12         //return 返回值;
13     });
14 
15     var t3 = t2.ContinueWith((t) =>
16     {
17         //具体操作3
18     });
19 
20     var t4 = t1.ContinueWith((t) =>
21     {
22         //具体操作4
23     });
24 
25     var t5 = t1.ContinueWith((t) =>
26     {
27         //具体操作5
28     });
29 
30     Task.WaitAll(t3, t4, t5);
31 }

-----------------------------------------------------------------------------------------

时间仓促,水平有限,如有不当之处,欢迎指正。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏xingoo, 一个梦想做发明家的程序员

Log4j官方文档翻译(五、日志输出的方法)

日志类提供了很多方法用于处理日志活动,它不允许我们自己实例化一个logger,但是提供给我们两种静态方法获得logger对象: public static Lo...

1728
来自专栏desperate633

设计模式之组合模式(Composite 模式)引入composite模式composite模式的具体实例composite模式小结

在计算机文件系统中,有文件夹的概念,文件夹里面既可以放入文件也可以放入文件夹,但是文件中却不能放入任何东西。文件夹和文件构成了一种递归结构和容器结构。 虽然文...

602
来自专栏大魏分享(微信公众号:david-share)

JavaEE中资源注入松耦合的实现 | 从开发角度看应用架构13

上下文和依赖注入(CDI)规范是Java EE规范中的许多从属规范之一。虽然CDI是在Java EE 6中引入的,但CDI背后的概念已经出现在各种框架中,包括S...

672
来自专栏coolblog.xyz技术专栏

MyBatis 源码分析系列文章导读

本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章。本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(...

611
来自专栏JackieZheng

探秘Tomcat——连接器和容器的优雅启动

前言: 上篇《探秘Tomcat——启动篇》粗线条的介绍了在tomcat在启动过程中如何初始化Bootstrap类,加载并执行server,从而启动整个tomc...

1998
来自专栏Jackson0714

干货分享:详解线程的开始和创建

2566
来自专栏大内老A

.NET Core的日志[1]:采用统一的模式记录日志

记录各种级别的日志是所有应用不可或缺的功能。关于日志记录的实现,我们有太多第三方框架可供选择,比如Log4Net、NLog、Loggr和Serilog 等,当然...

1946
来自专栏程序员的SOD蜜

使用Ring Buffer构建高性能的文件写入程序

最近常收到SOD框架的朋友报告的SOD的SQL日志功能报错:文件句柄丢失。经过分析得知,这些朋友使用SOD框架开发了访问量比较大的系统,由于忘记关闭SQL日志功...

2155
来自专栏苍云横渡学习笔记

【Demo】Gradle + Kotlin + Spring Boot + Mybatis + MySQL 实现 RESTful 数据库增删改查以及分页查询

这个Demo也可以说是一个轻量级服务器应用,其架构使用 Gradle + Kotlin + Spring Boot + Mybatis + MySQL。其中:

691
来自专栏Android 研究

APK安装流程详解5——Installer、InstallerConnection和Installd守护进程

因为Installer继承自SystemService,所以我们看下Installer的onStart方法 代码在Installer.java 396行

401

扫描关注云+社区