一. 整体说明
揭秘:
通过F12查看Task类的源码(详见下面的截图),发现Task类的构造函数有有一个参数为:TaskCreationOptions类型,本章节可以算作是一个扩展章节,主要就来研究TaskCreationOptions类的作用。
该类主要用来处理父子线程之间的关系,重要的几个参数如下:
①.AttachedToParent:指定将任务附加到任务层次结构中的某个父级,父任务必须等待所有子任务执行完毕才能执行
(下面的例子task线程必须等task1和task2线程执行完毕才能执行)
②. DenyChildAttach: 不允许子任务附加到父任务上
(下面例子task不再等待task1和task2,和00的默认效果相同)
③. HideScheduler: 子任务不使用父类Task的Scheduler,而是使用默认的 (不进行测试)
④. LongRunning:当已知是长时间运行的任务,可以使用该选项 (不进行测试)
⑤. PreferFairness:类似于队列的感觉,尽可能公平的方式安排任务 (不进行测试)
源码如下:
1 // 摘要: 2 // 指定可控制任务的创建和执行的可选行为的标志。 3 [Serializable] 4 [Flags] 5 public enum TaskCreationOptions 6 { 7 // 摘要: 8 // 指定应使用默认行为。 9 None = 0, 10 // 11 // 摘要: 12 // 提示 System.Threading.Tasks.TaskScheduler 以一种尽可能公平的方式安排任务,这意味着较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运行。 13 PreferFairness = 1, 14 // 15 // 摘要: 16 // 指定某个任务将是运行时间长、粗粒度的操作。 它会向 System.Threading.Tasks.TaskScheduler 提示,过度订阅可能是合理的。 17 LongRunning = 2, 18 // 19 // 摘要: 20 // 指定将任务附加到任务层次结构中的某个父级。 21 AttachedToParent = 4, 22 // 23 // 摘要: 24 // 如果尝试附有子任务到创建的任务,指定 System.InvalidOperationException 将被引发。 25 DenyChildAttach = 8, 26 // 27 // 摘要: 28 // 防止环境计划程序被视为已创建任务的当前计划程序。 这意味着像 StartNew 或 ContinueWith 创建任务的执行操作将被视为 System.Threading.Tasks.TaskScheduler.Default 29 // 当前计划程序。 30 HideScheduler = 16, 31 }
二. 实际测试
这里我们主要通过代码来比较默认情况下、AttachedToParent、DenyChildAttach之间的效果, task线程内部有task1和task2线程,并且在task内部开启。
1. 默认情况
1 { 2 Stopwatch watch = new Stopwatch(); 3 watch.Start(); 4 Console.WriteLine("----------------- Task多线程测试 --------------------------"); 5 Console.WriteLine("----------------- button1_Click 开始 主线程id为:{0} --------------------------", Thread.CurrentThread.ManagedThreadId); 6 7 #region 00-默认 8 { 9 Task task = new Task(() => 10 { 11 Task task1 = new Task(() => 12 { 13 Thread.Sleep(1000); 14 Console.WriteLine("我是task1线程"); 15 }); 16 Task task2 = new Task(() => 17 { 18 Thread.Sleep(1000); 19 Console.WriteLine("我是task2线程"); 20 }); 21 22 task1.Start(); 23 task2.Start(); 24 }); 25 26 task.Start(); 27 task.Wait(); //单个线程的等待 28 Console.WriteLine("------------------我是主线程--------------------"); 29 } 30 #endregion 31 32 watch.Stop(); 33 Console.WriteLine("----------------- button1_Click 结束 主线程id为:{0} 总耗时:{1}--------------------------", Thread.CurrentThread.ManagedThreadId, watch.ElapsedMilliseconds); 34 }
多次执行上述代码看效果:发现task线程执行完后,task1和task2才无序的执行。
2. AttachedToParent
作用:指定将任务附加到任务层次结构中的某个父级,父任务必须等待所有子任务执行完毕才能执行
1 { 2 Task task = new Task(() => 3 { 4 Task task1 = new Task(() => 5 { 6 Thread.Sleep(3000); 7 Console.WriteLine("我是task1线程"); 8 }, TaskCreationOptions.AttachedToParent); 9 Task task2 = new Task(() => 10 { 11 Thread.Sleep(3000); 12 Console.WriteLine("我是task2线程"); 13 }, TaskCreationOptions.AttachedToParent); 14 15 task1.Start(); 16 task2.Start(); 17 }); 18 19 task.Start(); 20 task.Wait(); //单个线程的等待 21 Console.WriteLine("------------------我是主线程--------------------"); 22 }
多次执行上述代码看效果:发现task线程必须等task1和task2执行完毕后才能执行(印证了AttachedToParent的作用),task1和task2无先后顺序。
3. DenyChildAttach
作用:不允许子任务附加到父任务上。
1 { 2 Task task = new Task(() => 3 { 4 Task task1 = new Task(() => 5 { 6 Thread.Sleep(3000); 7 Console.WriteLine("我是task1线程"); 8 }, TaskCreationOptions.AttachedToParent); 9 Task task2 = new Task(() => 10 { 11 Thread.Sleep(3000); 12 Console.WriteLine("我是task2线程"); 13 }, TaskCreationOptions.AttachedToParent); 14 15 task1.Start(); 16 task2.Start(); 17 }, TaskCreationOptions.DenyChildAttach); 18 19 task.Start(); 20 task.Wait(); //单个线程的等待 21 Console.WriteLine("------------------我是主线程--------------------"); 22 }
多次执行上述代码看效果:发现task线程执行完后,task1和task2才无序的执行。(和上述的默认情况是一致的)