快捷搜索:
来自 计算机编程 2019-06-15 16:34 的文章
当前位置: 67677新澳门手机版 > 计算机编程 > 正文

中TASK类的使用

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     }

推行如下:

67677新澳门手机版 1

注意

线程调用Wait方法时,系统一检查测线程要等待的Task是不是已经开头推行。假诺是线程则会阻塞直到Task运营甘休停止。但假诺Task还尚无起来推行任务,系统也许(取决于TaskScheduler)使用调用Wait的线程来实践Task,这种景况下调用Wait的线程不会堵塞,它会试行Task并即刻回去。好处在于未有线程会被封堵,所以收缩了资源占用。不好的地方在于插手线程在调用Wait前已经获取了贰个线程同步锁,而Task试图获取同一个锁,就能够招致死锁的线程。

28            // 子职责成功后,也出示任何未处理的不胜

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();

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

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

实行结果如下:

67677新澳门手机版 2

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() 使当前线程休眠给定时期。

 

16 17 18            //注释那些为了测试抛出的那些

参照文献 

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

...

 

20        {

任务集结重返值(WhenAll&WhenAny)

 Task中有极度有益的对相互运营的职务会集获取重返值的章程,比如WhenAllWhenAny

 8            //能够今日始发,也能够未来起头  

异常(AggregateException)

与线程分化,任务能够随时抛出特别。所以,假设任务中的代码抛出二个未管理至极,那么那些特别会活动传递到调用Wait()或Task<TResult>的Result属性的代码上。
职务的要命将会自动捕获并抛给调用者。为有限支撑报告具备的可怜,CLQX56会将极其封装在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属性包蕴了该错误。

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

此起彼落(continuation)&三番伍回选项(Task孔蒂nuationOptions)

延续(continuation)会告诉任务在成就后继续推行下边包车型大巴操作。一连平时由三个回调方法完结,它会在操作达成未来推行三遍。给贰个职务叠合一连的法子有二种

6                var tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);

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专程适用于并行编制程序场景。

43            {

职分概述

线程(Thread)是创办并发的平底工具,由此有早晚的局限性(不易获得再次来到值(必须透过创制共享域);极度的捕获和管理也麻烦;同一时间线程实施完结后不可能再次翻开该线程),那个局限性会稳中有降品质同时影响并发性的落到实处(不易于组合非常的小的出现操作完结很大的产出操作,会增添手工同步管理(加锁,发送复信号)的正视,轻巧出现难点)。

线程池的(ThreadPool)QueueUserWorkItem艺术很容发起三次异步的总计范围操作。但以此技术一样具有众多限制,最大的主题材料是从未有过内建的体制让您知道操作在哪天做到,也从未编写制定在操作完毕时获得再次回到值。

Task类能够化解上述全部的主题素材。

任务(Task)意味着二个通过或不经过线程完结的出现操作,任务是可构成的,使用延续(continuation)可将它们串联在共同,它们能够使用线程池收缩运维延迟,可应用回调方法幸免四个线程同期等待I/O密集操作。

 

TaskScheduler有八个派生类:thread pool task scheduler(线程池职责调节),和synchronization context task scheduler(同步上下文任务调节器)。默许情状下,所以应用程序使用的都以线程池职责调整器,那个任务调整器将职分调治给线程池的劳力线程。能够查询TaskScheduler的静态Default属性来博取对暗中同意职务调治器的多少个引用。

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     }

执行结果:

67677新澳门手机版 3

 

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

 结语

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

2.职务调治器(TaskScheduler)是Task之所以如此灵活的真面目,大家常说Task是在ThreadPool上更晋级化的卷入,其实十分的大程度上归功于那个目标,惦念下篇要不要说一下,但实际上本人看的都发烧...

3.Task类包蕴众多的重载,最棒F12跳到Task内熟谙下组织。

 

20                checked { sum = i; }

等待(Wait)&实施格局(TaskCreationOptions)

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

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。

 

61    }

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     }

实施结果:

67677新澳门手机版 4

10            tf.StartNew(() => Sum(cts.Token, 10000)),

职责撤除(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     }

举办理并了结果:

67677新澳门手机版 5

根据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.CancellationTokenSource67677新澳门手机版 ,构造函数能够指点参数钦定延迟多少日子后撤销职分。

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

任务绑定CancellationTokenSource指标,在Task源码中得以带领CancellationToken指标的启航职务措施都得以绑定CancellationTokenSource。

67677新澳门手机版 6

 

53            Int32 sum = 0;

基本功职分(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     }

实践结果:

67677新澳门手机版 7

地点示例中除了使用RunSynchronously方法运维的是一路职分(由启用的线程实施职分)外,别的二种办法之中都由线程池内的劳力线程处理。

说明

1.实际上Task.Factory类型本人便是TaskFactory(职分工厂),而Task.Run(在.NET4.5引进,4.0版本调用的是继承者)是Task.Factory.StartNew的简写法,是后者的重载版本,更灵活轻易些。

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

3.如Task.Run等艺术运转职责并不曾调用Start,因为它创设的是“热”职务,相反“冷”职责的创导是由此Task构造函数。

 

31                // 用StringBuilder输出全部

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     }

举办结果如下:

67677新澳门手机版 8

注意

假如使运营I/O密集职责,则能够动用TaskCompletionSource和异步函数(asynchronous functions),通过回调(三番伍遍)达成并发性,而是不通过线程实现。

举个例子使运转总结密集性职分,则可以应用叁个劳动者/消费者队列,调整这么些任务的产出数量,防止出现线程和进度阻塞的难点。

 

一起上下文职分调节器日常用于桌面应用程序,Winfrom,WPF及Silverlight。这一个义务调治器将多有职分都调治给应用程序的GUI线程,使具有义务代码都能得逞更新UI创设,举例开关、菜单项等。同步上下文任务调解器根本不使用线程池。同样,能够查询TaskScheduler的静态FromCurrentSynchronizationContext方法来获得对四个一头上下文职责调整器的引用。

返回值(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     }

施行如下:

 67677新澳门手机版 9

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

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

1  public TaskStatus Status { get; }

表达如下:

枚举值 说明
Canceled

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

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

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

 

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

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

67677新澳门手机版 10

 

21            Int32 sum = 0;

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     }

施行结果:

67677新澳门手机版 11

注意:若是频仍调用SetResult、SetException或SetCanceled,它们会抛出十二分,而TryXXX会重返false。

 

58            }

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     }

67677新澳门手机版 12

 1        static void Main(string[] args) 

21            {

25            }

3            CancellationTokenSource cts = new CancellationTokenSource();

9 10            t.Start();

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

24        }

50

3            Taskparent = new Task(() => {

37            }, TaskContinuationOptions.OnlyOnFaulted);

12            Console.WriteLine("开头实行子线程...{0}",i);

  ContinueWith讲完了。但是还尚未截止哦。  AttachedToParnt枚举类型(父职责)也不可能放过!看看怎么用,写法有一些奇异,看看: 

17                    childTasks[task].ContinueWith(t => cts.Cancel(), TaskContinuationOptions.OnlyOnFaulted);

25            }

 6                new Task(() => results[0] = Sum(10000), TaskCreationOptions.AttachedToParent).Start(); 

今昔看望TaskScheduler(任务调节)

25    }

55            {

17        }

职分调节有大多的,上边罗列部分,供仿照效法,越多的请参见  它蕴涵了汪洋的演示代码。

40            Int32 sum = 0;

ContinueWith?  啥东西~~??  要写可伸缩的软件,一定不能够使您的线程阻塞。那表示一旦调用Wait恐怕在职责未到位时查询Result属性,极有异常的大大概形成线程池创制二个新线程,那增大了资源的开支,并损害了紧缩性。  ContinueWith便是一个越来越好的章程,一个任务成功时它能够运行另一个职责。上边的例证不会阻塞任何线程。

40            parent.Start();

47 48                checked { sum = i; }

虽说职分提供了汪洋功效,但并不是不曾代价的。因为必须为具备的那些情状分配内存。假使没有供给义务提供的增大效能,使用ThreadPool.QueueUserWorkItem,能源的运用功能会越来越高级中学一年级些。Task类还完成了IDispose接口,允许你在用完Task对象后调用Dispose,然则大部分不管,让垃圾回收器回收就好。制造贰个Task对象时,代表Task唯一的二个Int32字段起首化为零,TaskID从1方始,每分配二个ID都递增1。顺带说一下,在您调节和测试中查看一个Task对象的时候,会导致调节和测试器展现Task的ID,从而致使为Task分配三个ID。  那么些ID的含义在于,每种Task都能够用三个唯一的值来标记。Visual Studio会在它的“并行职分”和相互货仓“窗口中显得那几个职责ID。要精通的是,那是Visual Studio本人分配的ID,不是在大团结代码中分配的ID,大约不容许将Visual Studio分配的ID和代码正在做的工香港作家联谊会系起来。要翻看本人正值运营的职分,能够在调整的时候查看Task的静态CurrentId属性,假如未有职务在实践,CurrentId再次回到null。  再看看TaskStatus的值,那一个能够查询Task对象的生存期:那么些在职分运营的时候都以足以一一查到的,还会有~决断要像这么:1 if(task.Status==TaskStatus.RantoCompletion)...为了简化编码,Task只提供多少个只读Boolean属性:IsCanceled,IsFaulted,IsCompleted,它们能回来最终状态true/false。假诺Task是经过调用某些函数来创立的,这几个Task对象就能够由于WaitingForActivation状态,它会活动运营。最后大家要来掌握一下TaskFactory(职务工厂):

21                tf.ContinueWhenAll(childTasks,

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

60        }

16            cwt.Wait();

52        {

32                checked { sum = i; }

24            return sum;

2        { 

 1        static void Main(string[] args) 

54            for (; n > 0; n--)

就疑似那样创立项目:

2        { 

8                new Task(() => results[2] = Sum(30000), TaskCreationOptions.AttachedToParent).Start(); 

52        }

41

23            {

51        private static Int32 Sum(CancellationToken ct, Int32 n)

33                StringBuilder sb = new StringBuilder("The following exception(s) occurred:" Environment.NewLine);

 4                var results = new Int32[3]; 

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

 这段代码大家应该猜得出是何等看头吧,人人都会写。  可是,笔者的结果为啥是t.Result而不直接是回来的Sum呢?  有未有小题大作的认为到?下边小编来讲说这段代码小编想发挥的情趣:  在三个线程调用Wait方法时,系统会检查线程要等待的Task是或不是曾经起来施行,假诺职务正在实行,那么那么些Wait方法会使线程阻塞,知道Task运维甘休截止。  就说上边的程序实施,因为增加数字太大,它抛出算术运算溢出荒唐,在一个妄想范围职务抛出二个未管理的极其时,那几个那么些会被“蕴涵”不并蕴藏到二个汇集中,而线程池线程是允许再次回到到线程池中的,在调用Wait方法还是Result属性时,这么些成员会抛出二个System.AggregateException对象。  以往你会问,为何要调用Wait恐怕Result?可能直接不查询Task的Exception属性?你的代码就恒久注意不到那一个可怜的产生,如若不可能捕捉到这一个可怜,垃圾回收时,抛出AggregateException,进度就能够即时停下,那就是“牵一动员全身”,岂有此理程序就协和关闭了,哪个人也不晓得那是哪些状态。所以,必须调用前边提到的某些成员,确认保障代码注意到那些,并从那当中平复。悄悄告诉您,其实在用Result的时候,内部会调用Wait。  怎么过来?  为了帮扶您检验未有留意到的特别,能够向TaskScheduler的静态UnobservedTaskException时间阶段贰个回调方法,当Task被垃圾回收时,若是出现三个尚未被注意到的格外,CL福睿斯终结器会抓住那么些事件。一旦引发,就能够向您的时日处理器方法传递二个UnobservedTaskException伊夫nArgs对象,个中蕴含了您从未注意的AggregateException。然后再调用UnobservedTasException伊芙nArgs的SetObserved方法来提议你的老大已经管理好了,从而阻碍CL卡宴终止进程。这是个图方便的做法,要少做那个,宁愿终止进度,也毫不呆着已经损坏的气象而持续运营。做人也一如今后,病了宁肯苏息,也决不带病坚定不移上班,你没那么高大,公司也不须求您的这点伟大,命是和煦的。(─.─|||扯远了。  除了单个等待任务,Task 还提供了八个静态方法:WaitAny和WaitAll,他们同意线程等待三个Task对象数组。  WaitAny方法会阻塞调用线程,知道数组中的任何三个Task对象完结,那些方法会重临多少个索引值,指明实现的是哪叁个Task对象。如若产生超时,方法将回来-1。它能够经过三个CancellationToken撤除,会抛出二个OperationCanceledException。  WaitAll方法也会阻塞调用线程,知道数组中的全数Task对象都完毕,倘若一切产生就重返true,假诺超时就回来false。当然它也能收回,一样会抛出OperationCanceledException。  说了如此多个裁撤职务的法子,今后来试试这些点子,加深下印象,修改先前例子代码,完整代码如下:

13          };

5  6            //能够明日开首,也得以以后起初  

11 12            var cwt = parent.ContinueWith( parentTask=>Array.ForEach(parentTask.Result,Console.WriteLine));

24                checked { sum = i; }

9                var childTasks = new[] {

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

14 15            cts.Cancel();//那是个异步请求,Task大概早已完毕了。笔者是双核机器,Task未遂过

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

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

 1        static void Main(string[] args) 

34                foreach (var e in p.Exception.Flatten().InnerExceptions)

10            });

38

9 10        private static void StartCode(object i)

5  

29            Int32 sum = 0;

31            {

20        {

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

8                //创设并运转3个子职分

9                return results;

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

义务工厂就这么用,便是贰个职分的集纳。

28        {

26            catch (AggregateException x)

28    }复制代码Oh,笔者都写晕了。。。( ﹏ )~例子中,父职分创建兵运转3个Task对象。私下认可情况下,一个义务创建的Task对象是一流职分,这一个职务跟成立它们的特别职务未有关联。TaskCreationOptions.AttachedToParent标记将一个Task和创立它的卓殊Task关联起来,除非全体子职责(子职务的子任务)截止运转,不然制造职分(父职务)不会认为已经收尾。调用ContinueWith方法创立贰个Task时,能够钦赐TaskContinuationOptions.AttachedToParent标记将一连职务置顶为多个子职分。

啊,你会意识结果是同等的。再来看看那一个是如何:TaskCreationOptions那个类型是三个枚举类型,传递一些标记来调整Task的实行格局。TaskCreationOptions定义如下:慢点,注释很详细,看看那么些有低价,TaskScheduler(任务调节器)不懂没涉及,请继续往下看,小编会介绍的,但请留心,那个标记都只是部分提出而已,在调解多个Task时,恐怕会、也可能不会接纳那一个建议,可是有一条要注意:AttachedToParent标识,它总会得到Task采用,因为它和TaskScheduler本人无关。

当今自己要说的是,用线程池不是调用ThreadPool的QueueUserWorkItem方法,而是用职分来做相同的事:

5                //

59            return sum;

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

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

1.索要创立一组Task对象来共享一样的情景

 看了如此多职分的方法操作示例了,今后来挖挖义务之中社团:  每种Task对象都有一组结合职分意况的字段。  二个Int32 ID(只读属性)代表Task执市场价格况的三个Int32对父职责的贰个引用对Task创设时置顶TaskSchedule的贰个引用对回调方法的二个引用对要传给回调方法的目的的三个引用(通过Task只读AsyncState属性查询)对二个ExceptionContext的引用对七个马努alReset伊芙ntSlim对象的引用还会有没个Task对象都有对依赖供给创立的局地互补状态的叁个引用,补充状态包蕴那些:二个CancellationToken三个ContinueWithTask对象集合为抛出未管理十分的子职务,所预备的七个Task对象集结说了如此多,只想要我们明白:

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

3            

  5              

来看下这段代码:

14

48            }

18

8        } 

 6            Console.WriteLine("主线程运营到此!"); 

12            13        }

  4            //一千000000这一个数字会抛出System.AggregateException 

36        }

37    }

23                checked { sum = i; }

本文由67677新澳门手机版发布于计算机编程,转载请注明出处:中TASK类的使用

关键词: