澳门新萄京 12

澳门新萄京:中TASK类的使用

任务概述

线程(Thread)是创办并发的最底层工具,由此有早晚的局限性(不易得到再次来到值(必须通过创设分享域);非常的破获和拍卖也麻烦;同有的时候候线程施行实现后不能再度拉开该线程),那一个局限性会减低品质相同的时候影响并发性的贯彻(不轻便组合相当的小的产出操作达成相当的大的面世操作,会增添手工业同步管理(加锁,发送时域信号)的依赖,轻松并发难点)。

线程池的(ThreadPool)QueueUserWorkItem方法很容发起一回异步的图谋范围操作。但以此本领同样享有众多范围,最大的难点是未曾内建的建制让您掌握操作在如哪天候做到,也远非编写制定在操作达成时收获再次来到值。

Task类能够消除上述全数的主题素材。

任务(Task)意味着一个透过或不通过线程达成的出现操作,任务是可结合的,使用延续(continuation)可将它们串联在一块,它们能够使用线程池减弱运维延迟,可接纳回调方法制止四个线程同期等待I/O密集操作。

 

但是,在前天那篇博客中,我们要了然的是,QueueUserWorkItem那么些本事存在非常多限制。其中最大的题材是不曾七个内建的编写制定让你领会操作在怎么时候做到,也绝非二个机制在操作完结是猎取叁个再次来到值,这几个主题素材驱动我们都不敢启用那些技术。

基本功职分(Task)

微软在.NET 4.0 引入任务(Task)的概念。通过System.Threading.Tasks命名空间应用任务。它是在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     }

实行结果:

澳门新萄京 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<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     }

实施如下:

 澳门新萄京 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) 

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     }

推行结果:

澳门新萄京 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,因为休眠最短):

澳门新萄京 4

 

3            Console.WriteLine(“主线程运维”); 

等待(Wait)&实行措施(TaskCreationOptions)

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

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     }

推行如下:

澳门新萄京 5

注意

线程调用Wait方法时,系统一检查测线程要等待的Task是或不是已经起先进行。假若是线程则会阻塞直到Task运维甘休截止。但借使Task还尚无起来施行任务,系统大概(取决于TaskScheduler)使用调用Wait的线程来奉行Task,这种情状下调用Wait的线程不会堵塞,它会实践Task并即刻赶回。好处在于未有线程会被封堵,所以减弱了财富占用。倒霉的地点在于参与线程在调用Wait前已经获取了多个线程同步锁,而Task试图获取同叁个锁,就能够促成死锁的线程。

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

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     }

实行结果如下:

澳门新萄京 6

注意

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

万一使运维总结密集性职分,则能够使用二个劳动者/花费者队列,调控那个义务的面世数量,制止出现线程和进程阻塞的主题材料。

 

 6            Console.WriteLine(“主线程运维到此!”); 

一连(continuation)&连续选项(TaskContinuationOptions)

延续(continuation)会告诉任务在成功后继续实施上边包车型客车操作。接二连三经常由三个回调方法达成,它会在操作实现之后推行贰次。给四个职务叠合一连的不二等秘书技有三种

7            Thread.Sleep(1000); 

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.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)

3.承袭选项(Task孔蒂nuationOptions)

在使用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        {

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     }

实行结果:

澳门新萄京 7

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

 

12            Console.WriteLine(“开首实践子线程…{0}”,i);

职分撤消(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     }

实施结果:

澳门新萄京 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。

澳门新萄京 9

 

13            Thread.Sleep(一千);//模拟代码操作   

异步等待 (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     }

实践结果如下:

澳门新萄京 10

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

1.Thread.Sleep 是手拉手延迟,Task.Delay异步延迟。

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

3.Thread.Sleep不可能撤除,Task.Delay能够。

4. Task.Delay() 比 Thread.Sleep()
消耗更加多的财富,可是Task.Delay()可用来为形式重临Task类型;大概依靠CancellationToken打消标志动态撤除等待。

5. Task.Delay() 实质创设二个运维给按期期的职务, Thread.Sleep()
使当前线程休眠给按期期。

 

 14        }

异常(AggregateException)

与线程差异,职务能够每一日抛出极度。所以,假诺任务中的代码抛出一个未管理分外,那么那几个特别会活动传送到调用Wait()或Task<TResult>的Result属性的代码上。
职分的非常将会活动捕获并抛给调用者。为保险报告具有的卓殊,CL牧马人会将特别封装在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    }

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     }

澳门新萄京 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     }

执行结果:

澳门新萄京 12

 

来看下这段代码:

 结语

1.async和await那多少个重点字下篇记录。

2.职责调节器(TaskScheduler)是Task之所以如此灵活的本来面目,大家常说Task是在ThreadPool上更进级化的包裹,其实十分大程度上归功于这些目标,思量下篇要别讲一下,但实际小编看的都高烧…

3.Task类满含众多的重载,最棒F12跳到Task内熟知下协会。

 

1        static void Main(string[] args) 

参考文献 

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            

  4            //一千000000以此数字会抛出System.AggregateException 

5  

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

 8            //能够明天起来,也得以未来起首  

9 10            t.Start();

11 12            //Wait显式的等待一个线程完毕

13 14            t.Wait();

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

17        }

18 19        private static Int32 Sum(Int32 i)

20        {

21            Int32 sum = 0;

22            for (; i > 0; i–)

23                checked { sum += i; }

24            return sum;

25        }

26    }

 这段代码大家应该猜得出是何等意思呢,人人都会写。  可是,作者的结果为啥是t.Result而不直接是回去的Sum呢? 
有未有小题大做的感到?上边小编来讲说这段代码作者想表达的意味:  在一个线程调用Wait方法时,系统会检查线程要等待的Task是还是不是业已发轫实践,假设职务正在举办,那么那么些Wait方法会使线程阻塞,知道Task运转甘休截止。  就说上边的程序实行,因为加上数字太大,它抛出算术运算溢出荒唐,在二个乘除范围职责抛出一个未处理的十一分时,那几个非常会被“包罗”不并累积到一个聚集中,而线程池线程是同意重临到线程池中的,在调用Wait方法只怕Result属性时,那个成员会抛出二个System.AggregateException对象。  现在您会问,为啥要调用Wait恐怕Result?也许直接不查询Task的Exception属性?你的代码就恒久注意不到这一个极度的发出,倘若无法捕捉到那个丰盛,垃圾回收时,抛出AggregateException,进度就能够马上终止,那正是“牵一发动全身”,无缘无故程序就和谐关闭了,什么人也不知晓那是如何情状。所以,必须调用前面提到的有些成员,确定保证代码注意到极度,并从那么些中苏醒。悄悄告诉你,其实在用Result的时候,内部会调用Wait。  怎么过来?  为了救助你检查实验未有理会到的不胜,能够向TaskScheduler的静态UnobservedTaskException时间阶段八个回调方法,当Task被垃圾回收时,假若出现三个尚未被注意到的丰富,CLSportage终结器会抓住那么些事件。一旦抓住,就能够向你的光阴管理器方法传递二个UnobservedTaskException伊夫nArgs对象,在那之中满含了你从未留意的AggregateException。然后再调用UnobservedTasException伊夫nArgs的SetObserved方法来提出你的不行已经处理好了,进而阻碍CL安德拉终止进度。那是个图省事的做法,要少做这么些,宁愿终止进度,也毫不呆着已经损坏的景况而延续运营。做人也同样,病了宁肯停歇,也决不带病百折不挠上班,你没那么高大,公司也无需你的那点伟大,命是投机的。(─.─|||扯远了。  除了单个等待职责,Task
还提供了三个静态方法:WaitAny和WaitAll,他们同意线程等待贰个Task对象数组。  WaitAny方法会阻塞调用线程,知道数组中的任何贰个Task对象落成,这几个方法会再次来到三个索引值,指明完毕的是哪三个Task对象。倘若产生超时,方法将赶回-1。它能够由此一个CancellationToken撤消,会抛出三个OperationCanceledException。  WaitAll方法也会阻塞调用线程,知道数组中的全数Task对象都成功,如若全数产生就回到true,假使超时就回到false。当然它也能收回,同样会抛出OperationCanceledException。  说了那般多少个裁撤任务的点子,未来来试试这些措施,加深下印象,修改先前例子代码,完整代码如下:

 1        static void Main(string[] args) 

2        { 

3            CancellationTokenSource cts = new
CancellationTokenSource();

 4            

  5              

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

8  9            //可今后天上马,也得以未来开首 

10            11            t.Start();

12 13            //在后来的某部时刻,撤废CancellationTokenSource
以撤除Task

14 15           
cts.Cancel();//那是个异步须要,Task大概已经成功了。笔者是双核机器,Task未有水到渠成过

16 17 18            //注释这些为了测量试验抛出的丰盛

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

20            try

21            {

22                //假诺任务已经撤废了,Result会抛出AggregateException

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

25            }

26            catch (AggregateException x)

27            {

28                //将任何OperationCanceledException对象都实属已管理。

29                //其余任何特别都产生抛出二个AggregateException,在那之中

30                //只富含未管理的丰富

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

33                Console.WriteLine(“Sum was Canceled”);

34            }

35          36        }

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

39        {

40            Int32 sum = 0;

41            for (; i > 0; i–)

42            {

43                //在撤消标识引用的CancellationTokenSource上万一调用

44                //Cancel,上边这一行就能够抛出OperationCanceledException

45 46                ct.ThrowIfCancellationRequested();

47 48                checked { sum += i; }

49            }

50            51            return sum;

52        }

53    }

  这一个事例体现了三个任务在拓展的时候中途撤销的操作,作者觉着它很有意思,你尝试也会发掘。  Lamada表明式写这一个,是个亮点,得学学,将CancellationToken闭包变量“传递”。

 借使不用Lamada表明式,那难题还真不好化解:  Taskt = new Task(()
=> Sum(cts.Token,一千0), cts.Token);  Sum(cts.Token,10000)
内的Token须求和cts.Token关联起来,你仍是可以够想出怎么关联起来么?

  好,职务取消也讲玩了,来看个越来越好用的技术:

1        static void Main(string[] args) 

2        { 

3  4            Taskt = new Task(i => Sum((Int32)i),10000); 

5  6            //可以今日始于,也足以以往起首  

7              8            t.Start(); 

9 10            Task cwt = 
t.ContinueWith(task=>Console.WriteLine(“The sum
is:{0}”,task.Result));

11            cwt.Wait();

12            13        }

14 15        private static Int32 Sum(Int32 i)

16        {

17            Int32 sum = 0;

18            for (; i > 0; i–)

19            {

20                checked { sum += i; }

21            }

22            23            return sum;

24        }

25    }

ContinueWith? 
啥东西~~??  要写可伸缩的软件,相对无法令你的线程阻塞。那代表一旦调用Wait或许在职责未到位时查询Result属性,极有非常的大可能产生线程池创制贰个新线程,那增大了能源的花费,并损害了紧缩性。  ContinueWith便是三个更加好的法门,二个义务到位时它能够运维另二个职务。上边的事例不会阻塞任何线程。

 当Sum的义务到位时,那几个职务会运行另叁个职务以体现结果。ContinueWith会重返对新的Task对象的一个引用,所感到了见到结果,笔者急需调用一下Wait方法,当然你也能够查询下Result,大概接续ContinueWith,重临的这么些目的足以忽略,它独有是三个变量。  还要提出的是,Task对象内部含有了ContinueWith任务的一个集合。所以,实际上能够用二个Task对象来每每调用ContinueWith。职分到位时,全数ContinueWith任务都会步向线程池队列中,在结构孔蒂nueWith的时候大家得以观望二个TaskContinuationOptions枚举值,不可小看,看看它的定义:PrefereFairness是竭尽公平的意趣,正是较早调整的任务大概较早的运行,先来后到,将线程放到大局队列,便足以兑现那个效果。ExecuteSynchronously指同步实践,强制八个职分用同三个线程一前一后运转,然后就联手运转了。
看得是还是不是晕乎乎
?有那样多枚例如子,怎么调节啊?多看五遍,知道职分的应用情状,今后用起来百步穿杨~想学新技术,将要能耐住,技巧基础深厚。来看个例证,用用这一个枚举。

 1        static void Main(string[] args) 

2        { 

3            Taskt = new Task(i => Sum((Int32)i),10000); 

4  5            t.Start(); 

6  7            t.ContinueWith(task=>Console.WriteLine(“The sum
is:{0}”,task.Result), 8               
TaskContinuationOptions.OnlyOnRanToCompletion); 

9            10           
t.ContinueWith(task=>Console.WriteLine(“Sum
throw:”+task.Exception),11               
TaskContinuationOptions.OnlyOnFaulted);

12            13           
t.ContinueWith(task=>Console.WriteLine(“Sum was
cancel:”+task.IsCanceled),14               
TaskContinuationOptions.OnlyOnCanceled);

15            try

16            {

17                t.Wait();  // 测试用

18            }

19            catch (AggregateException)

发表评论

电子邮件地址不会被公开。 必填项已用*标注