.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 条评论
登录 后参与评论

相关文章

来自专栏抠抠空间

MySQL之表的约束

一 介绍 约束条件与数据类型的宽度一样,都是可选参数 作用:用于保证数据的完整性和一致性 主要分为: PRIMARY KEY (PK) 标识该字段为该表的...

2756
来自专栏机器学习算法与Python学习

SQL Server 学习笔记

之前学过一点数据库但由于一直没有使用忘得差不多了,最近重新复习一下相关的知识,把基本的语法YOU又看了一遍,为了强化记忆在写一遍~~~~~~ ? 基本的 se...

34015
来自专栏Thomas的技术专栏

Ffmpeg对sps/pps的解析和格式转换

H264流的 SPS(序列参数集Sequence Parameter Set)和PPS(图像参数集Picture Parameter Set)记录了视频的基本编...

1462
来自专栏Python

约束

一 介绍 约束条件与数据类型的宽度一样,都是可选参数 作用:用于保证数据的完整性和一致性 主要分为: PRIMARY KEY (PK) 标识该字段为该表的...

1848
来自专栏函数式编程语言及工具

Scalaz(46)- scalaz-stream 基础介绍

    scalaz-stream是一个泛函数据流配件库(functional stream combinator library),特别适用于函数式编程。sc...

1997
来自专栏函数式编程语言及工具

Scalaz(17)- Monad:泛函状态类型-State Monad

  我们经常提到函数式编程就是F[T]。这个F可以被视为一种运算模式。我们是在F运算模式的壳子内对T进行计算。理论上来讲,函数式程序的运行状态也应该是在这个运算...

2268
来自专栏待你如初见

JavaIO流输入输出流-字节流

FileInputStream fis = new FileInputStream(fileName);

751
来自专栏企鹅号快讯

数据库中间件 Sharding-JDBC 源码分析——SQL 解析之插入SQL

1. 概述 本文前置阅读: 《SQL 解析(一)之词法解析》 《SQL 解析(二)之SQL解析》 本文分享插入SQL解析的源码实现。 不考虑 INSERT SE...

1815
来自专栏lgp20151222

ResultSet相关ResultSetMetaData详细

DatabaseMetaData 有关整个数据库的信息:表名、表的索引、数据库产品的名称和版本、数据库支持的操作。 ResultSet 关于某个表的信息或一...

642
来自专栏数据结构与算法

小米OJ刷题日志

$f[i][j]$表示第一个队列匹配到了$i$位置,第二个队列匹配到了$j$位置是否可行

1952

扫码关注云+社区