职责(task)

承继(continuation)&再三再四选项(TaskContinuationOptions)

延续(continuation)会告诉任务在成就后继续试行下边包车型大巴操作。一而再平时由贰个回调方法落成,它会在操作实现现在试行二次。给二个职分叠合三番五次的艺术有三种

基础职分(Task)

微软在.NET 4.0 引入任务(Task)的定义。通过System.Threading.Tasks命名空间应用任务。它是在ThreadPool的基础上进展打包的。Task私下认可都以运用池化线程,它们都以后台线程,那意味主线程结束时其余职务也会随着告一段落。

起步3个任务有多种方法,如以下示例:

 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     }

施行结果:

图片 1

下边示例中除去使用RunSynchronously方法运行的是一路职责(由启用的线程实施职务)外,别的二种格局之中都由线程池内的劳引力线程处理。

说明

一.其实Task.Factory类型本人正是TaskFactory(任务工厂),而Task.Run(在.NET四.5引进,四.0本子调用的是后人)是Task.Factory.StartNew的简写法,是继承者的重载版本,越来越灵活简单些。

二.调用静态Run方法会自动创立Task对象并即刻调用Start

三.如Task.Run等措施运维职责并从未调用Start,因为它创设的是“热”任务,相反“冷”职务的创制是透过Task构造函数。

 

叁.再而三选项(TaskContinuationOptions)

在使用ContinueWith时方可钦点职务的持续选项即TaskContinuationOptions,它的前两个枚举类型与事先说的TaskCreationOptions枚举提供的标记一模二样,补充后续多少个枚举值:

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

 

ExecuteSynchronously是指同步施行,多个义务都在同几个=线程壹前一后的试行。

ContinueWith结合Task孔蒂nuationOptions使用的示范:

 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。

 

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,因为休眠最短):

图片 2

 

参照他事他说加以调查文献 

CLR via C#(第4版) Jeffrey Richter

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

果壳中的C# C#伍.0不可企及指南  Joseph Albahari

C#并发编程 杰出实例  Stephen Cleary

 

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,它极其适用于增添越来越多的接二连三。然后1旦任务出现错误,我们无法不一向管理AggregateException。

如若想让持续运转在统贰个线程上,必须钦命 TaskContinuationOptions.ExecuteSynchronously;不然它会弹回线程池。ContinueWith专程适用于并行编制程序场景。

职责概述

线程(Thread)是创设并发的尾部工具,由此有一定的局限性(不易得到重返值(必须通过创立共享域);至极的破获和拍卖也麻烦;同时线程推行达成后不能够再一次开启该线程),那么些局限性会降低质量同时影响并发性的兑现(不轻松组合极小的面世操作完成不小的出现操作,会增加手工业同步管理(加锁,发送时域信号)的注重,轻易并发难题)。

线程池的(ThreadPool)QueueUserWorkItem措施很容发起1次异步的乘除范围操作。但以此技艺同样颇具诸多限制,最大的标题是绝非内建的体制让你精通操作在怎么着时候做到,也尚未编写制定在操作完毕时收获再次回到值。

Task类能够消除上述所有的难点。

任务(Task)表示一个经过或不通过线程完毕的产出操作,任务是可构成的,使用延续(continuation)可将它们串联在同步,它们可以使用线程池减弱运转延迟,可接纳回调方法制止七个线程同时等待I/O密集操作。

 

2.Handle

 即使需求只捕获特定项目非凡,比量齐观抛其余门类的非常,Handle艺术为此提供了一种火速格局。

Handle接受三个predicate(十分断言),并在种种内部极度上运维此断言。

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

即使断言重返True,它以为该特别是“已管理”,当有着非常过滤之后:

一.倘若全数非常是已管理的,格外不会抛出。

二.只要存在非常未管理,就能够组织3个新的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     }

进行结果:

图片 3

 

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     }

执行结果:

图片 4

异步等待 (Task.Delay)

 异步等待特别实用,由此它产生Task类的一个静态方法

 常用的行使方法有二种,如下:

 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     }

施行结果如下:

图片 5

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

1.Thread.Sleep 是联合延迟,Task.Delay异步延迟。

二.Thread.Sleep 会阻塞线程,Task.Delay不会。

叁.Thread.Sleep无法裁撤,Task.Delay可以。

四. Task.Delay() 比 Thread.Sleep()
消耗越多的能源,但是Task.Delay()可用感到格局重临Task类型;或许依据CancellationToken撤除标志动态裁撤等待。

五. Task.Delay() 实质创设多个周转给定期间的任务, Thread.Sleep()
使当前线程休眠给定时间。

 

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

Task有一个泛型子类Task<TResult>,它同意职责回到一个值。调用Task.Run,传入一个Func<Tresult>代理或包容的拉姆da表达式,然后查询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     }

实行如下:

 图片 6

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

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

1  public TaskStatus Status { get; }

评释如下:

枚举值 说明
Canceled

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

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

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

 

义务打消(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     }

进行结果:

图片 7

根据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。

图片 8

 

一.职责等待(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     }

实行如下:

图片 9

注意

线程调用Wait方法时,系统一检查测线程要等待的Task是或不是曾经初步执行。若是是线程则会卡住直到Task运营甘休结束。但如果Task还一贯不起来实行职务,系统也许(取决于TaskScheduler)使用调用Wait的线程来实行Task,这种意况下调用Wait的线程不会阻塞,它会执行Task并立时回到。好处在于未有线程会被堵塞,所以收缩了能源占用。不佳的地方在于参预线程在调用Wait前早已获取了一个线程同步锁,而Task试图获取同一个锁,就可以产生死锁的线程。

 结语

一.async和await那多个器重字下篇记录。

二.职务调整器(TaskScheduler)是Task之所以如此灵活的武夷山真面目,大家常说Task是在ThreadPool上更进级化的包装,其实比一点都不小程度上归功于那些目的,思考下篇要不要说一下,但实际上本身看的都胃痛…

叁.Task类包涵众多的重载,最棒F1二跳到Task内熟悉下结构。

 

TaskCompletionSource

另1种制造职责的章程是行使TaskCompletionSource。它同意创立三个职分,并可以任务分发给使用者,并且那么些使用者能够选择该职责的别样成员。它的完成原理是通过二个得以手动操作的“附属”职分,用于提示操作完毕或出错的光阴。

TaskCompletionSource的真正功用是创制2个不绑定线程的职分(手动调节职分职业流,能够令你把创制职分和完结职责分别)

这种方法非常适合I/O密集作业:能够应用具备任务的独到之处(它们能够转移重临值、非常和继续),但不会在操作试行时期阻塞线程。

举个例子说,要是一个职务要求静观其变贰秒,然后回来拾,我们的方法会重回在八个二秒后成功的任务,通过给任务叠加三个一而再就足以在不封堵任何线程的前提下打字与印刷那一个结果,如下:

 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     }

实行结果:

图片 10

注意:纵然反复调用SetResult、SetException或SetCanceled,它们会抛出特别,而TryXXX会重返false。

 

1.GetAwaiter

任务的法子GetAwaiter是Framework
4.5新增加的,而C#
伍.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会回到3个等待者(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中。

叁.假如出现一块上下文,那么会自动捕捉它,然后继续提交到这么些上下文中。在不供给共同上下文的情况下一般不选拔这种办法,使用ConfigureAwait取代他。它常常会使延续运行在指引职分所在的线程上,从而制止不必要的过载。

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

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

 Task中有特别有益的对相互运转的职责集合获取重临值的办法,比方WhenAllWhenAny

异常(AggregateException)

与线程不一致,任务能够每十四日抛出卓殊。所以,假诺职务中的代码抛出三个未管理分外,那么这么些这个会自动传送到调用Wait()或Task<TResult>的Result属性的代码上。
职务的特别将会自行捕获并抛给调用者。为有限支撑报告富有的格外,CLWrangler会将非常封装在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品质,就能够不重复抛出格外而检查评定出错的任务。
壹.IsFaulted和IsCanceled都回来False,表示没错误发生。
二.IsCanceled为True,则义务抛出了OperationCanceledOperation(裁撤线程正在实施的操作时在线程中抛出的老大)。
三.IsFaulted为True,则任务抛出另1种卓殊,而Exception属性包罗了该错误。

等候(Wait)&实施办法(TaskCreationOptions)

二.职责实施办法(TaskCreationOptions)

咱俩通晓为了创造3个Task,必要调用构造函数并传递一个Action或Action<object>委托,要是传递的是愿意1个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     }

实践结果如下:

图片 11

注意

若果使运维I/O密集职责,则足以应用TaskCompletionSource和异步函数(asynchronous
functions),通过回调(接二连三)完结并发性,而是不经过线程达成。

假定使运转计算密集性任务,则能够选拔1个劳动者/消费者队列,调控这么些职务的面世数量,防止出现线程和进程阻塞的主题素材。

 

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     }

图片 12

相关文章