atp);? // Grandchild
? ? ? ? ? ? ? ? }, atp);
? ? ? ? ? ? });
? ? ? ? ? ? // The following call throws a NullReferenceException (wrapped
? ? ? ? ? ? // in nested AggregateExceptions):
? ? ? ? ? ? parent.Wait();
? ? ? ? }
? ? }
}
取消Task
如果想要支持取消任务,那么在创建Task的时候,需要传入一个CancellationTokenSouce
示例代码:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MultiThreadTest
{
? ? class Program
? ? {
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? var cancelSource = new CancellationTokenSource();
? ? ? ? ? ? CancellationToken token = cancelSource.Token;
? ? ? ? ? ? Task task = Task.Factory.StartNew(() =>
? ? ? ? ? ? {
? ? ? ? ? ? ? ? // Do some stuff...
? ? ? ? ? ? ? ? token.ThrowIfCancellationRequested();? // Check for cancellation request
? ? ? ? ? ? ? ? // Do some stuff...
? ? ? ? ? ? }, token);
? ? ? ? ? ? cancelSource.Cancel();
? ? ? ? ? ? try
? ? ? ? ? ? {
? ? ? ? ? ? ? ? task.Wait();
? ? ? ? ? ? }
? ? ? ? ? ? catch (AggregateException ex)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? if (ex.InnerException is OperationCanceledException)
? ? ? ? ? ? ? ? ? ? Console.Write("Task canceled!");
? ? ? ? ? ? }
? ? ? ? ? ? Console.ReadLine();
? ? ? ? }
? ? }
}
任务的连续执行
Continuations
任务调度也是常见的需求,Task支持一个任务结束之后执行另一个任务。
Task task1 = Task.Factory.StartNew(() => Console.Write("antecedant.."));
Task task2 = task1.ContinueWith(task =>Console.Write("..continuation"));
Continuations 和Task
Task也有带返回值的重载,示例代码如下:
Task.Factory.StartNew(() => 8)
? ? .ContinueWith(ant => ant.Result * 2)
? ? .ContinueWith(ant => Math.Sqrt(ant.Result))
? ? .ContinueWith(ant => Console.WriteLine(ant.Result));? // output 4
子任务
前面提到了,当你等待一个任务的时候,同时需要等待它的子任务完成。
下面代码演示了带子任务的Task:
using System;
using System.Threading.Tasks;
using System.Threading;
namespace MultiThreadTest
{
? ? class Program
? ? {
? ? ? ? public static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? Task parentTask = Task.Factory.StartNew(() =>
? ? ? ? ? ? {
? ? ? ? ? ? ? ? int[] results = new int[3];
? ? ? ? ? ? ? ? Task t1 = new Task(() => { Thread.Sleep(3000); results[0] = 0; }, TaskCreationOptions.AttachedToParent);
? ? ? ? ? ? ? ? Task t2 = new Task(() => { Thread.Sleep(3000); results[1] = 1; }, TaskCreationOptions.AttachedToParent);
? ? ? ? ? ? ? ? Task t3 = new Task(() => { Thread.Sleep(3000); results[2] = 2; }, TaskCreationOptions.AttachedToParent);
? ? ? ? ? ? ? ? t1.Start();
? ? ? ? ? ? ? ? t2.Start();
? ? ? ? ? ? ? ? t3.Start();
? ? ? ? ? ? ? ? return results;
? ? ? ? ? ? });
? ? ? ? ? ? Task finalTask = parentTask.ContinueWith(parent =>
? ? ? ? ? ? {
? ? ? ? ? ? ? ? foreach (int result in parent.Result)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? Console.WriteLine(result);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? });
? ? ? ? ? ? finalTask.Wait();
? ? ? ? ? ? Console.ReadLine();
? ? ? ? }
? ? }
}
这段代码的输出结果是: 1,2,3
FinalTask会等待所有子Task结束后再执行。
TaskFactory
关于TaskFactory,上面的例子中我们使用了System.Threading.Tasks .Task.Factory属性来快速的创建Task。当然你也可以自己创建TaskFactory,你可以指定自己的TaskCreationOptions,TaskContinuationOptions来使得通过你的Factory创建的Task默认行为不同。
.Net中有一些默认的创建Task的方式,由于TaskFactory创建Task的默认行为不同可能会导致一些不容易发现的问题。
如在.NET 4.5中,Task加入了一个Run的静态方法:
Task.Run(someAction);
如果你用这个方法代替上面例子中的Task.Factory.StartNew,就无法得到正确的结果。原因是Task.Run创建Task的行为默认是默认是拒绝添加子任务的。上面的代码等价于:
? ? Task.Factory.StartNew(someAction, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
你也可以创建具有自己默认行为的TaskFactory。
无论ThreadPool也好,或者Task,微软都是在想进办法来实现线程的重用,来节省不停的创建销毁线程带来的开销。线程池内部的实现可能在不同版本中有不同的机制。如果可能的话,使用线程池来管理线程仍然是建议的选择。
我们主要介绍了一下Task的基本用法,在我们编程过程中,有一些使用Task来提升程序性能的场景往往是很相似的,微软为了简化编程,在System.Threading.Tasks.Parallel中封装了一系列的并行类,内部也是通过Task来实现