一. 整體說明
揭秘:
該章節的性質和上一個章節類似,也是一個擴展的章節,主要來研究Task類下的實例方法ContinueWith中的參數TaskContinuationOptions。
通過F12查看TaskContinuationOptions的源碼,知道主要有這么幾個參數:
①. LazyCancellation:在延續取消的情況下,防止延續的完成直到完成先前的任務。
(下面的例子task2取消,原先的延續關系不復存在,task1和task3可以並行執行)
②. ExecuteSynchronously:希望執行前面那個task的thread也在執行本延續任務
(下面的例子執行task2的Thread和執行task1的Thread是同一個,所有二者的線程id相同)
③. NotOnRanToCompletion和OnlyOnRanToCompletion
NotOnRanToCompletion:延續任務必須在前面task非完成狀態才能執行
OnlyOnRanToCompletion:延續任務必須在前面task完成狀態才能執行
(下面例子:注釋掉異常的這句代碼task2不能執行,task3能執行;不注釋,task2能執行,task3不能執行)
源碼如下:

1 [Serializable] 2 [Flags] 3 public enum TaskContinuationOptions 4 { 5 // 摘要: 6 // Default = "Continue on any, no task options, run asynchronously" 指定應使用默認行為。 7 // 默認情況下,完成前面的任務之后將安排運行延續任務,而不考慮前面任務的最終 System.Threading.Tasks.TaskStatus。 8 None = 0, 9 // 10 // 摘要: 11 // 提示 System.Threading.Tasks.TaskScheduler 以一種盡可能公平的方式安排任務,這意味着較早安排的任務將更可能較早運行,而較晚安排運行的任務將更可能較晚運行。 12 PreferFairness = 1, 13 // 14 // 摘要: 15 // 指定某個任務將是運行時間長、粗粒度的操作。 它會向 System.Threading.Tasks.TaskScheduler 提示,過度訂閱可能是合理的。 16 LongRunning = 2, 17 // 18 // 摘要: 19 // 指定將任務附加到任務層次結構中的某個父級。 20 AttachedToParent = 4, 21 // 22 // 摘要: 23 // 如果嘗試附有子任務到創建的任務,指定 System.InvalidOperationException 將被引發。 24 DenyChildAttach = 8, 25 // 26 // 摘要: 27 // 防止環境計划程序被視為已創建任務的當前計划程序。 這意味着像 StartNew 或 ContinueWith 創建任務的執行操作將被視為 System.Threading.Tasks.TaskScheduler.Default 28 // 當前計划程序。 29 HideScheduler = 16, 30 // 31 // 摘要: 32 // 在延續取消的情況下,防止延續的完成直到完成先前的任務。 33 LazyCancellation = 32, 34 // 35 // 摘要: 36 // 指定不應在延續任務前面的任務已完成運行的情況下安排延續任務。 此選項對多任務延續無效。 37 NotOnRanToCompletion = 65536, 38 // 39 // 摘要: 40 // 指定不應在延續任務前面的任務引發了未處理異常的情況下安排延續任務。 此選項對多任務延續無效。 41 NotOnFaulted = 131072, 42 // 43 // 摘要: 44 // 指定只應在延續任務前面的任務已取消的情況下才安排延續任務。 此選項對多任務延續無效。 45 OnlyOnCanceled = 196608, 46 // 47 // 摘要: 48 // 指定不應在延續任務前面的任務已取消的情況下安排延續任務。 此選項對多任務延續無效。 49 NotOnCanceled = 262144, 50 // 51 // 摘要: 52 // 指定只應在延續任務前面的任務引發了未處理異常的情況下才安排延續任務。 此選項對多任務延續無效。 53 OnlyOnFaulted = 327680, 54 // 55 // 摘要: 56 // 指定只應在延續任務前面的任務已完成運行的情況下才安排延續任務。 此選項對多任務延續無效。 57 OnlyOnRanToCompletion = 393216, 58 // 59 // 摘要: 60 // 指定應同步執行延續任務。 指定此選項后,延續任務將在導致前面的任務轉換為其最終狀態的相同線程上運行。 如果在創建延續任務時已經完成前面的任務,則延續任務將在創建此延續任務的線程上運行。 61 // 只應同步執行運行時間非常短的延續任務。 62 ExecuteSynchronously = 524288, 63 }
二. 實際測試
下面通過代碼來說明默認情況下、LazyCancellation、ExecuteSynchronously、NotOnRanToCompletion和OnlyOnRanToCompletion的作用和效果。
1. 默認情況
默認情況下,task1執行完后→task2→task2執行完后→task3。
1 { 2 Task task1 = new Task(() => 3 { 4 Thread.Sleep(1000); 5 Console.WriteLine("task1 tid={0}, dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now); 6 }); 7 8 var task2 = task1.ContinueWith(t => 9 { 10 Console.WriteLine("task2 tid={0}, dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now); 11 }); 12 13 var task3 = task2.ContinueWith(t => 14 { 15 Console.WriteLine("task3 tid={0}, dt={1} {2}", Thread.CurrentThread.ManagedThreadId, DateTime.Now, task2.Status); 16 }); 17 18 task1.Start(); 19 20 }
運行結果: task1執行完后→ task2執行→task2執行完后→ task3執行。
2. LazyCancellation
作用:取消該線程,該線程的前一個線程和后一個線程並行執行。
1 { 2 CancellationTokenSource source = new CancellationTokenSource(); 3 source.Cancel(); 4 5 Task task1 = new Task(() => 6 { 7 Thread.Sleep(1000); 8 Console.WriteLine("task1 tid={0}, dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now); 9 }); 10 11 var task2 = task1.ContinueWith(t => 12 { 13 Console.WriteLine("task2 tid={0}, dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now); 14 }, source.Token, TaskContinuationOptions.LazyCancellation, TaskScheduler.Current); 15 16 var task3 = task2.ContinueWith(t => 17 { 18 Console.WriteLine("task3 tid={0}, dt={1} {2}", Thread.CurrentThread.ManagedThreadId, DateTime.Now, task2.Status); 19 }); 20 21 task1.Start(); 22 23 }
運行結果: task2線程已經被取消,task1線程和task2線程並行執行。
3. ExecuteSynchronously
作用:希望執行前面那個task的thread也在執行本延續任務。
1 { 2 Task task1 = new Task(() => 3 { 4 Thread.Sleep(1000); 5 Console.WriteLine("task1 tid={0}, dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now); 6 }); 7 8 var task2 = task1.ContinueWith(t => 9 { 10 Console.WriteLine("task2 tid={0}, dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now); 11 }, TaskContinuationOptions.ExecuteSynchronously); 12 13 task1.Start(); 14 }
結果:task1和task2線程的線程id相同。
4. NotOnRanToCompletion和OnlyOnRanToCompletion
NotOnRanToCompletion:延續任務必須在前面task非完成狀態才能執行。
OnlyOnRanToCompletion:延續任務必須在前面task完成狀態才能執行。
{ Task task1 = new Task(() => { Thread.Sleep(1000); Console.WriteLine("task1 tid={0}, dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now); //手動制造異常,表示不能執行完畢 //(注釋掉這句話task2不能執行,task3能執行) //不注釋,task2能執行,task3不能執行 //throw new Exception("hello world"); }); var task2 = task1.ContinueWith(t => { Console.WriteLine("task2 tid={0}, dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now); }, TaskContinuationOptions.NotOnRanToCompletion); var task3 = task1.ContinueWith(t => { Console.WriteLine("task3 tid={0}, dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now); }, TaskContinuationOptions.OnlyOnRanToCompletion); task1.Start(); }
分析:task2和task3均為task的延續線程,當task1報錯時候,task2執行,task3不能執行;當task1正常時候,task2不能執行,task3能執行。
task1報錯時的運行結果:
task2正常時的運行結果: