有时候 需创建一组具有相同状态的Task对象, 可使用System.Threading.Tasks命名空间中TaskFactory<TResult>类型和TaskFactory类型,两者均派生自System.Object。所有任务可共享的属性有:CancellationToken,TaskScheduler, TaskCreationgOption, TaskContinuation.
对此有以下测试目的:
1. 任务工厂内各任务是否异步执行
2. CancellationTokeSource类对协作式的影响
3. 父子任务的影响关系
4. 任务错误信息的捕获
5. ContinueWith 与 ContinueWhenAll的使用
using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;
using System.Threading.Tasks;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Task parent = new Task(() =>
{
var cts = new CancellationTokenSource();
var tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
var childrenTasks = new[]
{
tf.StartNew(() => sum(cts.Token,500)),
tf.StartNew(() => sum(cts.Token, 100)),
tf.StartNew(() => sum(cts.Token, 1000)),
tf.StartNew(() => sum(cts.Token, 200)),
tf.StartNew(() => sum(cts.Token, 3000)),
tf.StartNew(() => sum(cts.Token, 400)),
tf.StartNew(() => sum(cts.Token, 800)),
};
for (int task = 0; task < childrenTasks.Length; task++)
childrenTasks[task].ContinueWith(t => cts.Cancel(), TaskContinuationOptions.OnlyOnFaulted);
tf.ContinueWhenAll(childrenTasks,
completedTasks => completedTasks.Where(
t => !t.IsFaulted && !t.IsCanceled).Max(t => t.Result), CancellationToken.None).ContinueWith(
t => Console.WriteLine("The max is: " + t.Result),
TaskContinuationOptions.ExecuteSynchronously);
});
parent.ContinueWith(p =>
{
StringBuilder sb = new StringBuilder("The following exception(s) occurred: " + Environment.NewLine);
if (p.Exception != null)
{
foreach (var e in p.Exception.Flatten().InnerExceptions)
sb.AppendLine(" " + e.GetType().ToString());
}
Console.WriteLine(sb.ToString());
}, TaskContinuationOptions.OnlyOnFaulted);
parent.Start();
Console.ReadKey();
}
private static int sum(CancellationToken token, int n)
{
int sum = 0;
int tmp = n;
Console.WriteLine();
Console.WriteLine(n);
Thread.Sleep(1000);
for (; n > 0; n--)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("\nCount is cancelled");
break;
}
checked { sum += n; }
Console.Write(" n" + n.ToString());
}
Console.WriteLine("\n" + tmp.ToString() + "End!");
return sum;
}
分析:
1. 代码分为两部分 第二部分用于计算求和,且随时根据token值决定是否结束当前任务;第一部分创建父子线程
2. 父线程parent下利用TaskFactory创建若干同属性线程,均采用默认任务调度方式
3. for循环通知所有线程,如果一个线程faulted其余线程均停止工作。
childrenTasks[task].ContinueWith(t => cts.Cancel(), TaskContinuationOptions.OnlyOnFaulted);
本身便是个任务,只是这个任务只有满足条件TaskContinuationOptions.OnlyOnFaulted才会执行Action:cts.Cancel()。
4. 当childrenTasks所有任务完成后,取出没故障的(!t.IsFaulted)并不被取消的(!t.IsCanceled)的所有任务的最大值,并打印到屏幕上来。 注意之所以调用CancellationToken.None是因为tf.ContinueWhenAll仍属于parent的子任务,会受到cts.Token影响,所以通过覆盖CancellationToken.None以达到一直显示的目的。
5 parent在所有子任务的sum计算完成后(而不等待tf.ContinueWhenAll(...)执行完成)显示错误信息。
可得到以下结论:
1. 如下图: 任务工厂内各个任务异步执行;parent.ContinueWith与tf.ContinueWhenAll无直接关系。
2. 更改代码为
var childrenTasks = new[]
{
tf.StartNew(() => sum(cts.Token,500)),
tf.StartNew(() => sum(cts.Token, 100)),
tf.StartNew(() => sum(cts.Token, Int32.MaxValue)),
tf.StartNew(() => sum(cts.Token, 200)),
tf.StartNew(() => sum(cts.Token, 3000)),
tf.StartNew(() => sum(cts.Token, 400)),
tf.StartNew(() => sum(cts.Token, 800)),
};
可得到
从而看出,当某一任务故障后导致cts.Cancel()执行;因此有两个任务(计数为200和3000)被终结.
3.推测:(不知是否正确)
父任务的结束标志是所有子任务完成,但不包括子任务所启动的任务。