职分(task)

2019-12-10 03:44栏目:编程
TAG:

职务概述

线程(Thread)是开创并发的平功底具,因而有分明的局限性(不易获得再次来到值(必得透过创制分享域);分外的抓获和拍卖也麻烦;同期线程实行达成后不能够再一次开启该线程),那一个局限性会下降质量同不经常候影响并发性的兑现(不易于组合十分的小的现身操作完成非常的大的产出操作,会大增手工业同步管理(加锁,发送确定性信号)的依附,轻易出现难点)。

线程池的(ThreadPool)QueueUserWorkItem主意很容发起三回异步的测算范围操作。但以此技巧相通具有广大限量,最大的标题是不曾内建的机制让您精晓操作在如何时候做到,也平素不编写制定在操作完成时获得重回值。

Task类能够缓慢解决上述全数的难题。

任务(Task)代表二个通过或不通过线程达成的面世操作,任务是可组成的,使用延续(continuation)可将它们串联在联合,它们能够使用线程池减弱运维延迟,可利用回调方法幸免多少个线程同时等待I/O密集操作。

 

可是,在几日前那篇博客中,我们要精通的是,QueueUserWorkItem那一个工夫存在许多范围。此中最大的主题素材是从未有过一个内建的体制让您知道操作在如何时候做到,也一向不一个编制在操作落成是赢得一个重返值,那个主题素材驱动大家都不敢启用这几个技巧。

1 System.Threading.Tasks.Task简介

幼功职分(Task)

微软在.NET 4.0 引入任务(Task)的定义。通过System.Threading.Tasks命名空间应用bob体育平台,任务。它是在ThreadPool的底工上扩充打包的。Task私下认可都以应用池化线程,它们都以后台线程,那意味着主线程截止时别的任务也会跟着告意气风发段落。

早先三个任务有多样措施,如以下示例:

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Console.WriteLine("主线程Id:{0}", Thread.CurrentThread.ManagedThreadId);
 6             int workerThreadsCount, completionPortThreadsCount;
 7             ThreadPool.GetAvailableThreads(out workerThreadsCount, out completionPortThreadsCount);
 8             Console.WriteLine("剩余工作线程数:{0},剩余IO线程数{1}", workerThreadsCount, completionPortThreadsCount);
 9             //第一种:实例化方式Start启动
10             {
11                 Task task = new Task(() =>
12                 {
13                     Test("one-ok");
14                 });
15                 task.Start();
16             }
17             //第二种:通过Task类静态方法Run方式进行启动
18             {
19                 Task.Run(() =>
20                 {
21                     Test("two-ok");
22                 });
23             }
24             //第三种:通过TaskFactory的StartNew方法启动
25             {
26                 TaskFactory taskFactory = new TaskFactory();
27                 taskFactory.StartNew(() =>
28                 {
29                     Test("three-ok");
30                 });
31             }
32             //第四种:.通过Task.Factory进行启动
33             {
34                 Task taskStarNew = Task.Factory.StartNew(() =>
35                 {
36                     Test("four-ok");
37                 });
38             }
39             //第五种:通过Task对象的RunSynchronously方法启动(同步,由主线程执行,会卡主线程)
40             {
41                 Task taskRunSync = new Task(() =>
42                 {
43                     Console.WriteLine("线程Id:{0},执行方法:five-ok", Thread.CurrentThread.ManagedThreadId);
44                 });
45                 taskRunSync.RunSynchronously();
46             }
47             Thread.Sleep(1000);
48             ThreadPool.GetAvailableThreads(out workerThreadsCount, out completionPortThreadsCount);
49             Console.WriteLine("剩余工作线程数:{0},剩余IO线程数{1}", workerThreadsCount, completionPortThreadsCount);
50             Console.ReadKey();
51         }
52         static void Test(string o)
53         {
54             Thread.Sleep(2000);
55             Console.WriteLine("线程Id:{0},执行方法:{1}", Thread.CurrentThread.ManagedThreadId, o);
56         }
57         /*
58          * 作者:Jonins
59          * 出处:http://www.cnblogs.com/jonins/
60          */
61     }

举办结果:

bob体育平台 1

上边示例中除去使用RunSynchronously方法运转的是联合签字职分(由启用的线程实行任务)外,此外二种方式之中都由线程池内的劳力线程处理。

说明

1.实际Task.Factory类型本人就是TaskFactory(任务工厂),而Task.Run(在.NET4.5引进,4.0本子调用的是后世)是Task.Factory.StartNew的简写法,是后人的重载版本,越来越灵敏简单些。

2.调用静态Run方法会自动创制Task对象并及时调用Start

3.如Task.Run等艺术运转义务并从未调用Start,因为它创立的是“热”职务,相反“冷”任务的创办是因此Task布局函数。

 

Microsoft为了战胜这个限定(同一时候消除别的部分主题素材),引入了职务(tasks)的概念。顺带说一下大家得经过System.Threading.Tasks命名空间来接受它们。

三个Task表示八个异步操作,Task的始建和实践是单身的。

返回值(Task<TResult>)&状态(Status)

Task有三个泛型子类Task<TResult>,它同意职务再次回到二个值。调用Task.Run,传入二个Func<Tresult>代理或同盟的Lambda表明式,然后查询Result属性拿到结果。假使职分未有完成,那么访问Result属性会梗塞当前线程,直至任务实现

1     public static Task<TResult> Run<TResult>(Func<TResult> function);

而职务的Status属性可用来追踪职分的推市场价格况,如下所示:

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Task<int> task = Task.Run(() =>
 6             {
 7                 int total = 0;
 8                 for (int i = 0; i <= 100; i++)
 9                 {
10                     total += i;
11                 }
12                 Thread.Sleep(2000);
13                 return total;
14             });
15             Console.WriteLine("任务状态:{0}",task.Status);
16             Thread.Sleep(1000);
17             Console.WriteLine("任务状态:{0}", task.Status);
18             int totalCount = task.Result;//如果任务没有完成,则阻塞
19             Console.WriteLine("任务状态:{0}", task.Status);
20             Console.WriteLine("总数为:{0}",totalCount);
21             Console.ReadKey();
22         }
23     }

执行如下:

 bob体育平台 2

Reulst属性内部会调用Wait(等待);

任务的Status属性是一个TaskStatus枚举类型:

1  public TaskStatus Status { get; }

证实如下:

枚举值 说明
Canceled

任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 对取消进行了确认,此时该标记处于已发送信号状态;

或者在该任务开始执行之前,已向该任务的 CancellationToken 发出了信号。

Created 该任务已初始化,但尚未被计划。
Faulted 由于未处理异常的原因而完成的任务。
RanToCompletion 已完成执行的任务。
Running 任务正在运行,尚未完成。
WaitingForActivation 该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划。
WaitingForChildrenToComplete 该任务已完成执行,正在隐式等待附加的子任务完成。
WaitingToRun 该任务已被计划执行,但尚未开始执行。

 

方今自家要说的是,用线程池不是调用ThreadPool的QueueUserWorkItem方法,而是用职务来做相符的事:

只读属性:

职务集合再次来到值(WhenAll&WhenAny)

 Task中有那多少个有利的对互相运维的职分会集获取重回值的方法,比如WhenAllWhenAny

复制代码 1        static void Main(string[] args) 

返回值

名称

说明

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

1.WhenAll

WhenAll:等候提供的富有 Task 对象完毕施行进度(全体职责总体实现)。

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             List<Task<int>> taskList = new List<Task<int>>();//声明一个任务集合
 6             TaskFactory taskFactory = new TaskFactory();
 7             for (int i = 0; i < 5; i++)
 8             {
 9                 int total = i;
10                 Task<int> task = taskFactory.StartNew(() => Test(total));
11                 taskList.Add(task);//将任务放进集合中
12             }
13             Console.WriteLine("主线程Id:{0},继续执行A.....", Thread.CurrentThread.ManagedThreadId);
14             Task<int[]> taskReulstList = Task.WhenAll(taskList);//创建一个任务,该任务将集合中的所有 Task 对象都完成时完成
15             for (int i = 0; i < taskReulstList.Result.Length; i++)//这里调用了Result,所以会阻塞线程,等待集合内所有任务全部完成
16             {
17                 Console.WriteLine("返回值:{0}", taskReulstList.Result[i]);//遍历任务集合内Task返回的值
18             }
19             Console.WriteLine("主线程Id:{0},继续执行B.....", Thread.CurrentThread.ManagedThreadId);
20             Console.ReadKey();
21         }
22         private static int Test(int o)
23         {
24             Console.WriteLine("线程Id:{0},Task执行成功,参数为:{1}", Thread.CurrentThread.ManagedThreadId, o);
25             Thread.Sleep(500 * o);
26             return o;
27         }
28     }

试行结果:

bob体育平台 3

2        { 

 

2.WhenAny

WhenAny:等待提供的任风流倜傥 Task 对象实现试行进程(只要有贰个任务成功)。

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             List<Task<int>> taskList = new List<Task<int>>();//声明一个任务集合
 6             TaskFactory taskFactory = new TaskFactory();
 7             for (int i = 0; i < 5; i++)
 8             {
 9                 int total = i;
10                 Task<int> task = taskFactory.StartNew(() => Test(total));
11                 taskList.Add(task);//将任务放进集合中
12             }
13             Console.WriteLine("主线程Id:{0},继续执行A.....", Thread.CurrentThread.ManagedThreadId);
14             Task<Task<int>> taskReulstList = Task.WhenAny(taskList);//创建一个任务,该任务将在集合中的任意 Task 对象完成时完成
15             Console.WriteLine("返回值:{0}", taskReulstList.Result.Result);//得到任务集合内最先完成的任务的返回值
16             Console.WriteLine("主线程Id:{0},继续执行B.....", Thread.CurrentThread.ManagedThreadId);
17             Console.ReadKey();
18         }
19         private static int Test(int o)
20         {
21             Console.WriteLine("线程Id:{0},Task执行成功,参数为:{1}", Thread.CurrentThread.ManagedThreadId, o);
22             Thread.Sleep(500 * o);
23             return o;
24         }
25     }

实行结果(这里重临值断定会是0,因为休眠最短):

bob体育平台 4

 

3            Console.WriteLine("主线程运转"卡塔尔(قطر‎; 

2 Task状态和生命周期

等候(Wait)&实施措施(TaskCreationOptions)

4            //ThreadPool.QueueUserWorkItem(StartCode,5); 

三个Task实例只会做到其生命周期二遍,当Task达到它的3种恐怕的末尾状态之不时,它就再也回不去早先的情况了。职分的生命周期从TaskStatus.Created状态确实起首。

1.职分等待(Wait)

调用职责的Wait方法能够隔断职责直至职责成功,相同于线程的join。

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Task task = Task.Run(() =>
 6             {
 7                 Console.WriteLine("线程执行Begin");
 8                 Thread.Sleep(2000);
 9                 Console.WriteLine("线程执行End");
10             });
11             Console.WriteLine("任务是否完成:{0}", task.IsCompleted);
12             task.Wait();//阻塞,直至任务完成
13             Console.WriteLine("任务是否完成:{0}", task.IsCompleted);
14             Console.ReadKey();
15         }
16     }

实行如下:

bob体育平台 5

注意

线程调用Wait方法时,系统一检查测线程要等待的Task是不是已经起来推行。假诺是线程则会卡住直到Task运营结束甘休。但假若Task还一直不起来实践任务,系统恐怕(决意于TaskScheduler)使用调用Wait的线程来试行Task,这种情形下调用Wait的线程不会窒碍,它会实行Task并当即回到。好处在于未有线程会被拥塞,所以裁减了能源占用。不佳的地方在于加入线程在调用Wait前早就拿到了二个线程同步锁,而Task试图获取同一个锁,就可以产生死锁的线程。

5            new Task(StartCode, 5).Start();

1) 开始状态

2.职务执行措施(TaskCreationOptions)

小编们领略为了创建五个Task,必要调用构造函数并传递贰个Action或Action<object>委托,假诺传递的是愿意一个Object的法子,还必得向Task的结构函数穿都要传给操作的实参。还足以选用向结构器传递一些TaskCreationOptions标志来调节Task的实行方式。

 TaskCreationOptions为枚举类型

枚举值 说明
None 默认。
PreferFairness 尽可能公平的方式安排任务,即先进先执行。
LongRunning 指定任务将是长时间运行的,会新建线程执行,不会使用池化线程。
AttachedToParent 指定将任务附加到任务层次结构中的某个父级
DenyChildAttach 任务试图和这个父任务连接将抛出一个InvalidOperationException
HideScheduler 强迫子任务使用默认调度而非父级任务调度

在默许情状下,Task内部是运营在池化线程上,这种线程会特别符合实践短总括密集作业。假使要试行长梗塞操作,则要防止选拔池化线程。

在池化线程上运维三个长职分难点一点都不大,然而倘诺要同不常间运行多个长职分(特别是会堵塞的义务),则会对质量发生默转潜移。最棒应用:TaskCreationOptions.LongRunning。

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             int workerThreadsCount, completionPortThreadsCount;
 6             ThreadPool.GetAvailableThreads(out workerThreadsCount, out completionPortThreadsCount);
 7             Console.WriteLine("剩余工作线程数:{0},剩余IO线程数{1},主线程Id:{2}", workerThreadsCount, completionPortThreadsCount, Thread.CurrentThread.ManagedThreadId);
 8             Task task = Task.Factory.StartNew(() =>
 9             {
10                 Console.WriteLine("长任务执行,线程Id:{0}", Thread.CurrentThread.ManagedThreadId);
11                 Thread.Sleep(2000);
12             }, TaskCreationOptions.LongRunning);
13             Thread.Sleep(1000);
14             ThreadPool.GetAvailableThreads(out workerThreadsCount, out completionPortThreadsCount);
15             Console.WriteLine("剩余工作线程数:{0},剩余IO线程数{1},主线程Id:{2}", workerThreadsCount, completionPortThreadsCount, Thread.CurrentThread.ManagedThreadId);
16             Console.ReadKey();
17         }
18     }

实施结果如下:

bob体育平台 6

注意

设若使运维I/O密集职分,则足以选用TaskCompletionSource和异步函数(asynchronous functions),通过回调(三回九转)达成并发性,而是不通过线程实现。

生龙活虎旦使运转总括密集性职务,则能够选拔三个劳动者/消费者队列,调控这么些任务的面世数量,防止现身线程和进度拥塞的题材。

 

 6            Console.WriteLine("主线程运维到此!"卡塔尔; 

Task实例有两种恐怕的开头状态

持续(continuation)&三番两次选项(TaskContinuationOptions)

延续(continuation)会告诉任务在成功后继续实践上面包车型地铁操作。三回九转日常由一个回调方法实现,它会在操作完毕之后实施一遍。给四个职分叠合一而再连续的不二法门有三种

7            Thread.Sleep(1000); 

说明

TaskStatus.Created

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

TaskStatus.WaitingForActivation

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

TaskStatus.WaitingToRun

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

1.GetAwaiter

职务的办法GetAwaiter是Framework 4.5新扩展的,而C# 5.0的异步成效应用了这种艺术,由此它相当重大。给贰个职务叠合延续如下:

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Task<int> task = Task.Run(() =>
 6              {
 7                  int total = 0;
 8                  for (int i = 0; i <= 100; i++)
 9                  {
10                      total += i;
11                  }
12                  Thread.Sleep(2000);
13                  return total;
14              });
15             var awaiter = task.GetAwaiter();
16             awaiter.OnCompleted(() =>
17             {
18                 int result = awaiter.GetResult();//在延续中获取Task的执行结果
19                 Console.WriteLine(result);
20             });
21             Console.ReadKey();
22         }
23     }

奉行结果决定台会打字与印刷:5050。

调用GetAwaiter会回到三个等待者(awaiter)对象,它会让指引(antecedent)职务在职务到位(或出错)之后施行二个代理。已经达成的职务也得以叠合一个接续,那件事三番两次会立时实行。

注意

1.等待者(awaiter)能够是随机对象,但必得包涵特定的四个方法和叁个Boolean类型属性。

1   public struct TaskAwaiter<TResult> : ICriticalNotifyCompletion, INotifyCompletion
2     {
3         public bool IsCompleted { get; }
4         public TResult GetResult();
5         public void OnCompleted(Action continuation);
6     }

2.引导职务出现错误,那么当一而再代码调用awaiter.GetResult()时就能够再度抛出十三分。我们能够必要调用GetResult,而是直接待上访谈初叶任务的Result属性(task.Result)。

GetResult的益处是,当向导职务现身谬误时,非凡能够一直抛出而不封装在AggregateException中。

3.假使现身生机勃勃道上下文,那么会自动捕捉它,然后继续提交到这么些上下文中。在无需协同上下文的情景下平日不采纳这种艺术,使用ConfigureAwait代替他。它经常会使延续运行在教导任务所在的线程上,进而幸免不必要的过载。

1    var awaiter = task.ConfigureAwait(false).GetAwaiter();

8        } 

2)中间状态

2.ContinueWith

另意气风发种附加一而再再三再四的秘技是调用任务的ContinueWith方法:

 1         static void Main(string[] args)
 2         {
 3             Task<int> task = Task.Run(() =>
 4             {
 5                 int total = 0;
 6                 for (int i = 0; i <= 100; i++)
 7                 {
 8                     total += i;
 9                 }
10                 Thread.Sleep(2000);
11                 return total;
12             });
13             task.ContinueWith(continuationAction =>
14             {
15                 int result = continuationAction.Result;
16                 Console.WriteLine(result);
17             });
18             Console.ReadKey();
19         }

ContinueWith本人会回去贰个Task,它那多少个适用于加多越来越多的世襲。然后若是任务现身谬误,大家必须一直管理AggregateException。

借使想让持续运营在统多个线程上,必须钦赐 TaskContinuationOptions.ExecuteSynchronously;不然它会弹回线程池。ContinueWith极其适用于并行编制程序场景。

9 10        private static void StartCode(object i)

Task实例有二种恐怕的中间状态

3.持续选项(TaskContinuationOptions)

在使用ContinueWith时方可钦定职务的接续选项即TaskContinuationOptions,它的前多少个枚举类型与事情发生前说的TaskCreationOptions枚举提供的申明完全相仿,补充后续几个枚举值:

枚举值 说明
LazyCancellation 除非先导任务完成,否则禁止延续任务完成(取消)。
NotOnRanToCompletion 指定不应在延续任务前面的任务已完成运行的情况下安排延续任务。
NotOnFaulted 指定不应在延续任务前面的任务引发了未处理异常的情况下安排延续任务。
NotOnCanceled 指定不应在延续任务前面的任务已取消的情况下安排延续任务。 
OnlyOnCanceled 指定只应在延续前面的任务已取消的情况下安排延续任务。
OnlyOnFaulted 指定只有在延续任务前面的任务引发了未处理异常的情况下才应安排延续任务。
OnlyOnRanToCompletion 指定只有在延续任务前面的任务引发了未处理异常的情况下才应安排延续任务。
ExecuteSynchronously 指定希望由先导任务的线程执行,先导任务完成后线程继续执行延续任务。

 

ExecuteSynchronously是指同步实践,四个职责都在同多个=线程大器晚成前生机勃勃后的试行。

ContinueWith结合TaskContinuationOptions使用的现身说法:

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Task<int> task = Task.Run(() =>
 6             {
 7                 int total = 0;
 8                 for (int i = 0; i <= 100; i++)
 9                 {
10                     total += i;
11                 }
12                 if (total == 5050)
13                 {
14                     throw new Exception("错误");//这段代码可以注释或开启,用于测试
15                 }
16                 return total;
17             });
18             //指定先导任务无报错的延续任务
19             task.ContinueWith(continuationAction =>
20             {
21                 int result = continuationAction.Result;
22                 Console.WriteLine(result);
23             }, TaskContinuationOptions.NotOnFaulted);
24             //指定先导任务报错时的延续任务
25             task.ContinueWith(continuationAction =>
26             {
27                 foreach (Exception ex in continuationAction.Exception.InnerExceptions)//有关AggregateException异常处理后续讨论
28                 {
29                     Console.WriteLine(ex.Message);
30                 }
31             }, TaskContinuationOptions.OnlyOnFaulted);
32             Console.ReadKey();
33         }
34     }

施行结果会打字与印刷:报错,借使注释掉抛出特别的代码则会打字与印刷5050。

 

11        {

说明

TaskStatus.Running

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

TaskStatus.WaitingForChildrenToComplete

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

TaskCompletionSource

另风度翩翩种创制任务的秘籍是选择TaskCompletionSource。它同意创设三个任务,并得以职责分发给使用者,何况这一个使用者能够运用该任务的别的成员。它的兑现原理是经过三个足以手动操作的“从属”职责,用于指示操作实现或出错的日子。

TaskCompletionSource的着实作用是创设二个不绑定线程的职责(手动调整职务专门的学问流,可以使您把成立任务和产生职责分别)

这种方法特别切合I/O密集作业:能够选取具备义务的长处(它们能够转移重返值、非凡和持续),但不会在操作履行时期拥塞线程。

比如说,借使一个职务须求等待2秒,然后回到10,大家的方法会重回在一个2秒后完结的天职,通过给职务叠合叁个继承就能够在不打断任何线程的前提下打字与印刷那些结果,如下:

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             var awaiter = Demo(2000).GetAwaiter();//得到任务通过延续输出返回值
 6             awaiter.OnCompleted(() =>
 7             {
 8                 Console.WriteLine(awaiter.GetResult());
 9             });
10             Console.WriteLine("主线程继续执行....");
11             Console.ReadKey();
12         }
13         static Task<int> Demo(int millis)
14         {
15             //创建一个任务完成源
16             TaskCompletionSource<int> taskCompletionSource = new TaskCompletionSource<int>();
17             var timer = new System.Timers.Timer(millis) { AutoReset = false };
18             timer.Elapsed += delegate
19             {
20                 timer.Dispose(); taskCompletionSource.SetResult(10);//写入返回值
21             };
22             timer.Start();
23             return taskCompletionSource.Task;//返回任务
24         }
25     }

推行结果:

bob体育平台 7

注意:若是每每调用SetResult、SetException或SetCanceled,它们会抛出特别,而TryXXX会重返false。

 

12            Console.WriteLine("最初推行子线程...{0}",i卡塔尔(英语:State of Qatar);

 

职务打消(CancellationTokenSource)

局部意况下,后台职务恐怕运维非常短日子,撤除任务就卓殊实用了。.NET提供了少年老成种标准的天职打消机制可用以凭仗任务的异步情势

取消基于CancellationTokenSource类,该类可用来发送撤消需要。央求发送给援用CancellationToken类的天职,个中CancellationToken类与CancellationTokenSource类相关联。

动用示举个例子下:

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             //构造函数 指定延迟2秒后自动取消任务
 6             CancellationTokenSource source = new CancellationTokenSource(2000);
 7             //注册一个任务取消后执行的委托
 8             source.Token.Register(() =>
 9             {
10                 Console.WriteLine("线程Id:{0} 任务被取消后的业务逻辑正在运行", Thread.CurrentThread.ManagedThreadId);
11             });
12             //启动任务,将取消标记源带入参数
13             Task.Run(() =>
14             {
15                 while (!source.IsCancellationRequested)//IsCancellationRequested为True时取消任务
16                 {
17                     Thread.Sleep(100);
18                     Console.WriteLine("线程Id:{0} 任务正在运行", Thread.CurrentThread.ManagedThreadId);
19                 }
20             }, source.Token);
21             //主线程挂起2秒后手动取消任务
22             {
23                 //Thread.Sleep(2000);
24                 //source.Cancel();//手动取消任务
25             }
26             //主线程不阻塞,2秒后自动取消任务
27             {
28                 source.CancelAfter(2000);
29             }
30             Console.ReadKey();
31         }
32     }

进行结果:

bob体育平台 8

根据Register措施绑定职分裁撤后的寄托

1   public CancellationTokenRegistration Register(Action callback);
2   public CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext);
3   public CancellationTokenRegistration Register(Action<object> callback, object state);
4   public CancellationTokenRegistration Register(Action<object> callback, object state, bool useSynchronizationContext);

手动撤消职务Cancel方法

机动撤废任务

1.CancelAfter措施后边能够教导参数钦命延迟多少后时间收回职责。

1   public void CancelAfter(TimeSpan delay);
2   public void CancelAfter(int millisecondsDelay);

2.CancellationTokenSource布局函数能够引导参数钦赐延迟多少时间后收回任务。

1   public CancellationTokenSource(TimeSpan delay);
2   public CancellationTokenSource(int millisecondsDelay);

职务绑定CancellationTokenSource对象,在Task源码中得以带领CancellationToken目的的起步职责措施都得以绑定CancellationTokenSource。

bob体育平台 9

 

13            Thread.Sleep(1000卡塔尔;//模拟代码操作   

3) 谈起底状态

异步等待 (Task.Delay)

 异步等待特别实用,因而它成为Task类的贰个静态方法

 常用的施用方法有2种,如下:

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             //第1种
 6             {
 7                 Task.Delay(2000).ContinueWith((o) =>
 8                 {
 9                     Console.WriteLine("线程Id:{0},异步等待2秒后执行的逻辑", Thread.CurrentThread.ManagedThreadId);
10                 });
11             }
12             //第2种
13             {
14                 Task.Delay(3000).GetAwaiter().OnCompleted(() =>
15                 {
16                     Console.WriteLine("线程Id:{0},异步等待3秒后执行的逻辑", Thread.CurrentThread.ManagedThreadId);
17                 });
18             }
19             Console.WriteLine("主线程Id:{0},继续执行", Thread.CurrentThread.ManagedThreadId);
20             Console.ReadKey();
21         }
22     }

奉行结果如下:

bob体育平台 10

Task.DelayThread.Sleep的异步版本。而它们的不相同如下(引自 禅道 ):

1.Thread.Sleep 是一块延迟,Task.Delay异步延迟。

2.Thread.Sleep 会堵塞线程,Task.Delay不会。

3.Thread.Sleep不能够取消,Task.Delay能够。

4. Task.Delay(卡塔尔(英语:State of Qatar) 比 Thread.Sleep()消耗越来越多的能源,但是Task.Delay(卡塔尔(英语:State of Qatar)可用于为艺术重返Task类型;可能依靠CancellationToken撤销标识动态裁撤等待。

5. Task.Delay(卡塔尔(قطر‎ 实质创制三个运行给定时期的任务, Thread.Sleep(卡塔尔使当前线程休眠给准时间。

 

 14        }

Task实例有二种也许的末尾状态

异常(AggregateException)

与线程不一致,职务可以随时抛出非常。所以,假若职责中的代码抛出八个未管理十分,那么这一个充裕会自行传送到调用Wait(卡塔尔(قطر‎或Task<TResult>的Result属性的代码上。
职分的可怜将会活动捕获并抛给调用者。为保险报告富有的非常,CL奔驰M级会将足够封装在AggregateException容器中,该容器公开的InnerExceptions质量中蕴藏全体捕获的百般,进而更适合併行编制程序。

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             try
 6             {
 7                 Task.Run(() =>
 8                 {
 9                     throw new Exception("错误");
10                 }).Wait();
11             }
12             catch (AggregateException axe)
13             {
14                 foreach (var item in axe.InnerExceptions)
15                 {
16                     Console.WriteLine(item.Message);
17                 }
18             }
19             Console.ReadKey();
20         }
21     }

上述示范调控台会展现:错误

注意

使用TaskIsFaultedIsCanceled天性,就能够不重复抛出拾贰分而检查测验出错的天职。
1.IsFaulted和IsCanceled都回去False,表示一向不错误发生。
2.IsCanceled为True,则任务抛出了OperationCanceledOperation(撤废线程正在施行的操作时在线程中抛出的那几个卡塔尔国。
3.IsFaulted为True,则职分抛出另大器晚成种特别,而Exception属性包括了该错误。

15    }

说明

TaskStatus.Canceled

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

TaskStatus.Faulted

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

TaskStatus.RunToCompletion

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

1.Flatten

当子职责抛出非常时,通过调用Flatten主意,能够消亡任性档期的顺序的嵌套以简化格外管理。

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             var parent = Task.Factory.StartNew(() =>
 6             {
 7                 int[] numbers = { 0 };
 8                 var childFactory = new TaskFactory(TaskCreationOptions.AttachedToParent, TaskContinuationOptions.None);
 9                 childFactory.StartNew(() => 10 / numbers[0]);//除零
10                 childFactory.StartNew(() => numbers[1]);//超出索引范围
11                 childFactory.StartNew(() => throw null);//空引用
12             });
13             try
14             {
15                 parent.Wait();
16             }
17             catch (AggregateException axe)
18             {
19                 foreach (var item in axe.Flatten().InnerExceptions)
20                 {
21                     Console.WriteLine(item.Message);
22                 }
23             }
24             Console.ReadKey();
25         }
26     }

bob体育平台 11

哎,你会发掘结果是均等的。再来看看那一个是何等:TaskCreationOptions那些类别是一个枚举类型,传递一些标注来决定Task的履增势势。TaskCreationOptions定义如下:慢点,注释很详细,看看那一个有裨益,TaskScheduler(职分调治器)不懂没涉及,请继续往下看,笔者会介绍的,但请介怀,这么些标志都只是部分建议而已,在调度二个Task时,大概会、也说不允许不会接收那么些建议,可是有一条要注意:AttachedToParent标志,它总会得到Task选用,因为它和TaskScheduler本人无关。

 

2.Handle

 假设急需只捕获特定类型非凡,一视同仁抛此外类型的十三分,Handle办法为此提供了生龙活虎种快捷格局。

Handle选择一个predicate(相当断言),并在各类内部极度上运营此断言。

1 public void Handle(Func<Exception, bool> predicate);

只要断言重返True,它以为该非常是“已管理”,当全数特别过滤之后:

1.只要持有非常是已管理的,万分不会抛出。

2.若是存在特别未管理,就能够组织叁个新的AggregateException对象来含有那些相当并抛出。

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             var parent = Task.Factory.StartNew(() =>
 6             {
 7                 int[] numbers = { 0 };
 8                 var childFactory = new TaskFactory(TaskCreationOptions.AttachedToParent, TaskContinuationOptions.None);
 9                 childFactory.StartNew(() => 10 / numbers[0]);//除零
10                 childFactory.StartNew(() => numbers[1]);//超出索引范围
11                 childFactory.StartNew(() => throw null);//空引用
12             });
13             try
14             {
15                 try
16                 {
17                     parent.Wait();
18                 }
19                 catch (AggregateException axe)
20                 {
21                     axe.Flatten().Handle(ex =>
22                     {
23                         if (ex is DivideByZeroException)
24                         {
25                             Console.WriteLine("除零-错误处理完毕");
26                             return true;
27                         }
28                         if (ex is IndexOutOfRangeException)
29                         {
30                             Console.WriteLine("超出索引范围-错误处理完毕");
31                             return true;
32                         }
33                         return false;//所有其它 异常重新抛出
34                     });
35 
36                 }
37             }
38             catch (AggregateException axe)
39             {
40                 foreach (var item in axe.InnerExceptions)//捕获重新抛出的异常
41                 {
42                     Console.WriteLine(item.Message);
43                 }
44             }
45             Console.ReadKey();
46         }
47     }

推行结果:

bob体育平台 12

 

来看下这段代码:

3 成立并试行职务

 结语

1.async和await这多少个第一字下篇记录。

2.职务调解器(TaskScheduler)是Task之所以如此灵活的庐山面目目,我们常说Task是在ThreadPool上更晋级化的卷入,其实不小程度上归功于那几个目的,考虑下篇要别讲一下,但事实上笔者看的都脑瓜疼...

3.Task类富含众多的重载,最佳F12跳到Task内了然下构造。

 

1        static void Main(string[] args) 

1)public Task StartNew(Action action)

参照文献 

CLR via C#(第4版) Jeffrey Richter

C#高级编制程序(第10版) C# 6 & .NET Core 1.0   Christian Nagel  

果壳中的C# C#5.0高不可攀指南  Joseph Albahari

C#并发编制程序 卓绝实例  Stephen Cleary

...

 

2        { 

参数:

3            

  action:要异步实施的操作委托

  4            //1000000000这几个数字会抛出System.AggregateException 

返回值:

5  

  已开发银行的 System.Threading.Tasks.Task

6            Taskt = new Task(n => Sum((Int32)n), 1000000000); 

异常:

  System.ArgumentNullException:当 action 参数为 null 时引发的不行。

 8            //可从前些天开端,也得以未来起头  

2)public static Task Run(Action action)

9 10            t.Start();

参数:

11 12            //Wait显式的守候三个线程完结

  action:表示在线程池推行的类别的任务

13 14            t.Wait();

返回值:

15            16            Console.WriteLine("The Sum is:"+t.Result);

  已开发银行的 System.Threading.Tasks.Task

17        }

异常:

18 19        private static Int32 Sum(Int32 i)

  System.ArgumentNullException:当 action 参数为 null 时引发的不得了。

20        {

3)public void Start()

21            Int32 sum = 0;

开发银行System.Threading.Tasks.Task,并将它配置到如今的 System.Threading.Tasks.TaskScheduler中试行。

22            for (; i > 0; i--)

异常:

23                checked { sum += i; }

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

24            return sum;

  System.InvalidOperationException:System.Threading.Tasks.Task 未处于有效情状,不能够运转。 它大概已开发银行、已推行或已收回,大概大概早已不帮衬以直接陈设的办法开创。

25        }

注意:

26    }

  仅使用Task的构造器来创设Task的实例并无法运转任务,还要选取Start才具开发银行职责。

 这段代码我们应该猜得出是什么样意思啊,人人都会写。  不过,小编的结果为何是t.Result而不直接是回去的Sum呢?  有未有为蛇画足的感到到?上边小编的话说这段代码笔者想表明的情趣:  在贰个线程调用Wait方法时,系统会检讨线程要等待的Task是不是早就上马进行,就算任务正在推行,那么这一个Wait方法会使线程梗塞,知道Task运转结束甘休。  就说上边包车型大巴程序实施,因为拉长数字太大,它抛出算术运算溢出荒谬,在二个思量范围职责抛出三个未管理的不得了时,那个可怜会被“富含”不并储存到二个会集中,而线程池线程是同意重返到线程池中的,在调用Wait方法可能Result属性时,这些成员会抛出八个System.AggregateException对象。  将来您会问,为什么要调用Wait只怕Result?只怕直接不查询Task的Exception属性?你的代码就恒久注意不到这么些极其的发生,即使无法捕捉到那么些丰裕,垃圾回笼时,抛出AggregateException,进度就能够即时停止,这就是“牵一动员全身”,莫名其妙程序就和睦关闭了,何人也不知底这是怎么着景况。所以,务必调用前边提到的有些成员,确定保证代码注意到不行,并从那么些中平复。悄悄告诉你,其实在用Result的时候,内部会调用Wait。  怎么过来?  为了救助你检查实验未有介意到的拾分,可以向TaskScheduler的静态UnobservedTaskException时间阶段两个回调方法,当Task被垃圾回笼时,要是现身贰个尚未被注意到的百般,CLCRUISER终结器会掀起这一个事件。少年老成旦引发,就能够向您的日子微处理机方法传递一个UnobservedTaskExceptionEvenArgs对象,当中含有了您未有在乎的AggregateException。然后再调用UnobservedTasException伊夫nArgs的SetObserved方法来建议你的要命已经管理好了,进而阻碍CLXC60终止进度。这是个图方便的做法,要少做这个,宁愿终止进度,也毫无呆着已经磨损的意况而持续运营。做人也雷同,病了宁肯苏息,也休想带病坚定不移上班,你没那么高大,公司也不供给你的那一点伟大,命是友好的。(─.─|||扯远了。  除了单个等待职务,Task 还提供了四个静态方法:WaitAny和WaitAll,他们同意线程等待二个Task对象数组。  WaitAny方法会梗塞调用线程,知道数组中的任何三个Task对象完结,这一个方法会重返贰个索引值,指明实现的是哪三个Task对象。倘诺爆发超时,方法将回来-1。它能够透过贰个CancellationToken撤销,会抛出四个OperationCanceledException。  WaitAll方法也会梗塞调用线程,知道数组中的全数Task对象都完毕,假使全数完了就赶回true,若是超时就回到false。当然它也能撤消,相近会抛出OperationCanceledException。  说了那般四个撤除职分的章程,以后来尝试那些法子,加深下印象,改过先前例子代码,完整代码如下:

4)Task.Factory.StartNew与Task.Run

 1        static void Main(string[] args) 

Task.Factory.StartNew重载方法提供愈来愈多的参数,能够垄断(monopoly卡塔尔什么安顿奉行任务以至如何向调节和测验器公开安插任务的机制和调节职务的创始和进行的可选行为。

2        { 

而Task.Run提供的法子则不负有上述决定机制。

3            CancellationTokenSource cts = new CancellationTokenSource();

 

 4            

4 等候职分达成

  5              

1)public void Wait()

6  7            Taskt = new Task(() => Sum(cts.Token,10000), cts.Token); 

等候 System.Threading.Tasks.Task 达成推行进度

8  9            //可现在天早先,也能够以往从前 

异常:

10            11            t.Start();

  ObjectDisposedException:Task 对象已被释放。

12 13            //在随后的某些时间,撤除CancellationTokenSource 以收回Task

  AggregateException:System.Threading.Tasks.Task 已撤废或在 System.Threading.Tasks.Task 的实践时期吸引了极度。如若任务已被注销,System.AggregateException将包蕴其 System.AggregateException.InnerExceptions 集结中的 System.OperationCanceledException。

14 15            cts.Cancel(卡塔尔(قطر‎;//那是个异步央浼,Task只怕曾经做到了。笔者是双核机器,Task未有做到过

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

16 17 18            //注释那一个为了测量试验抛出的不行

参数:

19            //Console.WriteLine("This sum is:" + t.Result);

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

20            try

异常:

21            {

  ObjectDisposedException:叁个或多少个 Task 中的对象 tasks 已被放飞。

22                //假使职责现已裁撤了,Result会抛出AggregateException

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

23 24                Console.WriteLine("This sum is:" + t.Result);

  AggregateException:在最少三个 Task 实例已废除。借使职分已被裁撤, AggregateException 十分包罗OperationCanceledException 中的极度其   AggregateException.InnerExceptions 集合。或在最少二个实施期间吸引了充分Task 实例。

25            }

说明:

26            catch (AggregateException x)

  主线程会等待作为参数字传送入的职分tasks实施完成才会奉行下一条语句。

27            {

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

28                //将任何OperationCanceledException对象都在说是已管理。

参数:

29                //其余任何特别都形成抛出叁个AggregateException,在这之中

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

30                //只包罗未管理的要命

异常:

31 32                x.Handle(e => e is OperationCanceledException);

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

33                Console.WriteLine("Sum was Canceled");

  System.ArgumentNullException:tasks 参数为 null。

34            }

  System.ArgumentException:tasks 参数满含 null 成分。

35          36        }

 

37 38        private static Int32 Sum(CancellationToken ct ,Int32 i)

5 撤消职务

39        {

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

40            Int32 sum = 0;

1)System.Threading.CancellationToken

41            for (; i > 0; i--)

流传有关应撤消操作的公告

42            {

属性:

43                //在撤销标识援用的CancellationTokenSource上只要调用

  public bool IsCancellationRequested { get; }

44                //Cancel,上边那黄金年代行就能抛出OperationCanceledException

方法:

45 46                ct.ThrowIfCancellationRequested();

  public void ThrowIfCancellationRequested();

47 48                checked { sum += i; }

  要是已倡议废除此标记,则抓住 System.OperationCanceledException。

49            }

异常:

版权声明:本文由bob体育app发布于编程,转载请注明出处:职分(task)