一. 整體說明
揭秘:
通過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才無序的執行。(和上述的默認情況是一致的)


