26.5.5 任务内部揭秘
在每个Task对象中,都包含代表Task唯一ID的一个Int32字段。创建一个Task对象时,字段初始化为零。第一次查询Task的只读Id属性时,属性将一个唯一Int32值分配给该字段,并从属性中返回它。TaskID从1开始,每分配一个ID都递增1。这个ID的意义在于,每个Task都可以用一个唯一的值来标识。运行一个任务的代码时,可以查询Task的静态CurrentId属性,它返回一个可空Int32(Int32?)。如果当前没有任务正在执行,查询CurrentId属性会返回null。
一个Task对象存在期间,可查询Task的只读Status属性了解它在其生存期的什么位置。这个属性返回一个TaskStatus值。TaskStatus值:Created、WaitingForActivation、WaitingToRun、Running、WaitingForChildrenToComplete、RanToCompletion、Canceled、Faulted。
一个Task或者Task<TResult>出错时,可以查询Task的Exception属性来获得任务抛出的未处理的异常。该属性总是返回一个AggregateException对象,它的包含了所有未处理的异常。
为简化编码,Task提供了几个只读的Boolean属性:IsCanceled、IsFaulted和IsCompleted。
如果Task是通过以下某个函数来创建的,这个Task对象就会处于WaitForActivation状态:ContinueWith、ContinueWhenAll、ContinueWhenAny或者FromAsync。
不能显式启动一个通过调用ContinueWith来创建的对象,这个Task会在它的先驱任务(antecedent task)执行完毕后自动开始。
26.5.6 任务工厂
如果要创建的是一组没有返回值的任务,那么要构造一个TaskFactory;如果要创建的是一组有一个特定返回值的任务,那么要构造一个TaskFactory<TResult>,并通过泛型TResult实参来传递任务的返回类型。创建任何工厂类时,要向它的构造器传递一些默认值。工厂创建的任务都将具有这些默认值。具体地说,要向任务工厂传递你希望工厂创建的任务具有的CancellationToken,TaskScheduler,TaskCreationOptions和TaskContinuationOptions设置。
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace ThreadStudy
{
class Program
{
static void Main(string[] args)
{
Task tp = new Task(() =>{
var cts = new CancellationTokenSource();
var tf = new TaskFactory<int>(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
var childTasks = new[]{
tf.StartNew(()=>Sum(cts.Token,100)),
tf.StartNew(()=>Sum(cts.Token,10)),
tf.StartNew(()=>Sum(cts.Token,10000))
};
for (int task = 0; task < childTasks.Length; ++task)
{
childTasks[task].ContinueWith(t => cts.Cancel(), TaskContinuationOptions.OnlyOnFaulted);
}
tf.ContinueWhenAll(childTasks, completedTasks => completedTasks.Where(t => !t.IsFaulted && !t.IsCanceled).Max(t => t.Result),CancellationToken.None)
.ContinueWith(t => Console.WriteLine("The maximum is: " + t.Result), TaskContinuationOptions.ExecuteSynchronously);
});
tp.Start();
Thread.Sleep(3000);
}
static int Sum(CancellationToken ct, int num)
{
ct.ThrowIfCancellationRequested();
if (num == 0)
return 0;
else
return num + Sum(ct, num - 1);
}
}
}
无论先驱任务是如何完成的,ContinueWhenAll和ContinueWhenAny都会执行延续任务。