Task中常见的两种枚举
Task构造函数中使用的枚举
public Task(Action action, TaskCreationOptions creationOptions);
public enum TaskCreationOptions
{
None = 0,
PreferFairness = 1,
LongRunning = 2,
AttachedToParent = 4,
DenyChildAttach = 8,
HideScheduler = 16,
RunContinuationsAsynchronously = 64
}
AttachedToParent
指定将任务附加到任务层次结构中的某个父级。
建立了父子关系,父任务想要继续执行,必须等待子任务执行完毕。其实等价于一个WaitAll操作
Task task = new Task(()=>
{
Task task1 = new Task(() =>
{
Thread.Sleep(100);
Console.WriteLine("t1");
}, TaskCreationOptions.AttachedToParent);
Task task2 = new Task(() =>
{
Thread.Sleep(10);
Console.WriteLine("t2");
}, TaskCreationOptions.AttachedToParent);
task1.Start();
task2.Start();
});
task.Start();
task.Wait();
Console.WriteLine("main");
输出顺序为t2 t1 main。此时task.Wait()等同于Task.WaitAll(task1,task2)。
如果去了枚举TaskCreationOptions.AttachedToParent,则输出结果为main t2 t1。
由此可以看出,AttachedToParent枚举标志的作用是让父线程等待其子线程执行完毕。
DenyChildAttach
不让子任务附加到父任务
Task task = new Task(()=>
{
Task task1 = new Task(() =>
{
Thread.Sleep(100);
Console.WriteLine("t1");
}, TaskCreationOptions.AttachedToParent);
Task task2 = new Task(() =>
{
Thread.Sleep(10);
Console.WriteLine("t2");
}, TaskCreationOptions.AttachedToParent);
task1.Start();
task2.Start();
},TaskCreationOptions.DenyChildAttach);
task.Start();
task.Wait();
Console.WriteLine("main");
输出顺序为:main t2 t1。因为最外层的父任务添加了DenyChildAttach标志。
HideScheduler
子任务默认不使用父类Task的Schedule,而是使用默认的
LongRunning
如果是长时间运行的任务,建议添加此选项。
同时,建议使用Thread,而不是ThreadPool,如果长期借用ThreadPool里的线程而不还,线程池为了满足需求会新开一些线程,这会导致ThreadPool的线程过多,销毁和调度都是一个很大的麻烦。
PreferFairness
提示 System.Threading.Tasks.TaskScheduler 以一种尽可能公平的方式安排任务,这意味着较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运
任务延续中使用的枚举
public Task ContinueWith(Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
public enum TaskContinuationOptions
{
None = 0,
PreferFairness = 1,
LongRunning = 2,
AttachedToParent = 4,
DenyChildAttach = 8,
HideScheduler = 16,
LazyCancellation = 32,
RunContinuationsAsynchronously = 64,
NotOnRanToCompletion = 65536,
NotOnFaulted = 131072,
OnlyOnCanceled = 196608,
NotOnCanceled = 262144,
OnlyOnFaulted = 327680,
OnlyOnRanToCompletion = 393216,
ExecuteSynchronously = 524288
}
其中有一部分枚举和构造函数中的枚举一样,用法也一样。
LazyCancellation
CancellationTokenSource source = new CancellationTokenSource();
source.Cancel();
Task task1 = new Task(() =>
{
Thread.Sleep(1000);
Console.WriteLine("task1,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
});
var task2 = task1.ContinueWith(t =>
{
Console.WriteLine("task2,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
},source.Token ,TaskContinuationOptions.LazyCancellation,TaskScheduler.Current);
var task3 = task2.ContinueWith(t =>
{
Console.WriteLine("task3,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
});
task1.Start();
输出顺序:task1 task3
在task2后的ContinueWith中加入TaskContinuationOptions.LazyCancellation会在task2被取消时,依然保持ContinueWith关系,等task1执行完才判断source.Token和task2是否被取消,使得task1 -> ask2 -> task3链不断,在执行完task1后再执行task1。
ExecuteSynchronously
指定应同步执行延续任务。 指定此选项后,延续任务在导致前面的任务转换为其最终状态的相同线程上运行。 如果在创建延续任务时已经完成前面的任务,则延续任务将在创建此延续任务的线程上运行。
通俗的说,这个枚举就是希望执行前面task的线程也来执行本延续任务。task2也希望使用task1的线程去执行,它可以防止线程切换即时间片切换。
Task task1 = new Task(() =>
{
Console.WriteLine("task1,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
});
var task2 = task1.ContinueWith(t =>
{
Console.WriteLine("task2,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
}, TaskContinuationOptions.ExecuteSynchronously);
执行结果:task1和task2均使用同一线程,tid的结果相同。
NotOnRanToCompletion、OnlyOnRanToCompletion
前面一个标志表示延续任务必须在前面task非完成状态才能执行
后面一个标志表示延续任务必须在前面task完成状态才能执行
Task task1 = new Task(() =>
{
Console.WriteLine("task1,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
throw new Exception("hello word");
});
var task2 = task1.ContinueWith(t =>
{
Console.WriteLine("task2,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
}, TaskContinuationOptions. OnlyOnRanToCompletion);
加入OnlyOnRanToCompletion后,由于task1抛出异常,所以task2不执行。
NotOnFaulted、OnlyOnFaulted
前面一个标志表示延续任务必须在前面task非失败状态才能执行
后面一个标志表示延续任务必须在前面task失败状态才能执行
Task task1 = new Task(() =>
{
Console.WriteLine("task1,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
throw new Exception("hello word");
});
var task2 = task1.ContinueWith(t =>
{
Console.WriteLine("task2,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
}, TaskContinuationOptions. OnlyOnFaulted);
加入OnlyOnFaulted后,虽然task1抛出异常,但是task2依然接着执行且只在task1失败抛出异常时才执行。
NotOnCanceled、OnlyOnCanceled
前面一个标志表示延续任务必须在前面task没被取消状态才能执行
后面一个标志表示延续任务必须在前面task被取消状态才能执行
总结
如果某些任务被取消,它既不是错误也不是完成,所以他不会被OnlyOnFaulted处理,但是会被NotOnRanToCompletion会被处理。
所以:
NotOnRanToCompletion | NotOnFaulted == OnlyOnCancelled
NotOnCanceled | NotOnFaulted == OnlyOnRanToCompletion
NotOnRanToCompletion | NotOnCanceld == OnlyOnFaulted
用这些枚举标记的好处是不用判断task的isComplete、isFaulted、isCanceled状态。