1. 程式人生 > 其它 >.Net多執行緒程式設計—任務Task

.Net多執行緒程式設計—任務Task

1 System.Threading.Tasks.Task簡介

一個Task表示一個非同步操作,Task的建立和執行是獨立的。

只讀屬性:

返回值

名稱

說明

object

AsyncState

表示在建立任務時傳遞給該任務的狀態資料

TaskCreationOptions

CreationOptions

獲取用於建立此任務的 TaskCreationOptions

CurrentId

當前正在執行 Task 的 ID

AggregateException

Exception

獲取導致 AggregateException 提前結束的 Task。如果 Task 成功完成或尚未引發任何異常,則返回 null

TaskFactory

Factory

提供對用於建立 Task 和 Task<TResult> 的工廠方法的訪問

int

Id

獲取此 Task 例項的 ID

bool

IsCanceled

指明此 Task 例項是否由於被取消的原因而已完成執行

bool

IsCompleted

指明此 Task 是否已完成

bool

IsFaulted

指明Task 是否由於未經處理異常的原因而完成

TaskStatus

Status

獲取此任務的 TaskStatus

2 Task狀態和生命週期

一個Task例項只會完成其生命週期一次,當Task達到它的3種可能的最終狀態之一時,它就再也回不去之前的狀態了。任務的生命週期從TaskStatus.Created狀態真正開始。

1) 初始狀態

Task例項有三種可能的初始狀態

說明

TaskStatus.Created

該任務已初始化,但尚未被計劃。使用Task建構函式建立Task例項時的初始狀態。

TaskStatus.WaitingForActivation

該任務正在等待 .NET Framework 基礎結構在內部將其啟用並進行計劃。一個任務的初始狀態,這個任務只有當其依賴的任務完成之後才會被排程。

TaskStatus.WaitingToRun

該任務已被計劃執行,但尚未開始執行。使用TaskFactory.StartNew建立的任務的初始狀態。

2)中間狀態

Task例項有兩種可能的中間狀態

說明

TaskStatus.Running

該任務正在執行,但尚未完成

TaskStatus.WaitingForChildrenToComplete

該任務已完成執行,正在隱式等待附加的子任務完成

3) 最終狀態

Task例項有三種可能的最終狀態

說明

TaskStatus.Canceled

該任務已通過對其自身的 CancellationToken 引發 OperationCanceledException 對取消進行了確認,此時該標記處於已傳送訊號狀態;或者在該任務開始執行之前,已向該任務的 CancellationToken 發出了訊號。Task屬性IsFaulted被設定為true

TaskStatus.Faulted

由於未處理異常的原因而完成的任務。Task屬性IsCanceled被設定為true

TaskStatus.RunToCompletion

已成功完成執行的任務。Task屬性IsCompleted被設定為true,IsFaulted和IsCanceled被設定為false

3 建立並執行任務

1)public Task StartNew(Action action)

引數:

  action:要非同步執行的操作委託

返回值:

  已啟動的 System.Threading.Tasks.Task

異常:

  System.ArgumentNullException:當 action 引數為 null 時引發的異常。

2)public static Task Run(Action action)

引數:

  action:表示線上程池執行的佇列的任務

返回值:

  已啟動的 System.Threading.Tasks.Task

異常:

  System.ArgumentNullException:當 action 引數為 null 時引發的異常。

3)public void Start()

啟動 System.Threading.Tasks.Task,並將它安排到當前的 System.Threading.Tasks.TaskScheduler中執行。

異常:

  System.ObjectDisposedException:已釋放 System.Threading.Tasks.Task 例項。

  System.InvalidOperationException:System.Threading.Tasks.Task 未處於有效狀態,無法啟動。 它可能已啟動、已執行或已取消,或者可能已經不支援以直接計劃的方式建立。

注意:

  僅使用Task的構造器來建立Task的例項並不能啟動任務,還要使用Start才能啟動任務。

4)Task.Factory.StartNew與Task.Run

Task.Factory.StartNew過載方法提供更多的引數,可以控制如何計劃執行任務以及如何向偵錯程式公開計劃任務的機制和控制任務的建立和執行的可選行為。

而Task.Run提供的方法則不具有上述控制機制。

4 等待任務完成

1)public void Wait()

等待 System.Threading.Tasks.Task 完成執行過程

異常:

  ObjectDisposedException:Task 物件已被釋放。

  AggregateException:System.Threading.Tasks.Task 已取消或在 System.Threading.Tasks.Task 的執行期間引發了異常。如果任務已被取消,System.AggregateException將包含其 System.AggregateException.InnerExceptions 集合中的 System.OperationCanceledException。

2)public static void WaitAll(params Task[] tasks)

引數:

  tasks:要等待的 Task 例項的陣列

異常:

  ObjectDisposedException:一個或多個 Task 中的物件 tasks 已被釋放。

  ArgumentNullException:tasks 引數為 null或tasks 引數包含 null 元素。

  AggregateException:在至少一個 Task 例項已取消。如果任務已被取消, AggregateException 異常包含 OperationCanceledException 中的異常其   AggregateException.InnerExceptions 集合。或在至少一個執行期間引發了異常 Task 例項。

說明:

  主執行緒會等待作為引數傳入的任務tasks執行結束才會執行下一條語句。

3)public static int WaitAny(params Task[] tasks)

引數:

  tasks:要等待的 Task 例項的陣列

異常:

  System.ObjectDisposedException:System.Threading.Tasks.Task 已被釋放。

  System.ArgumentNullException:tasks 引數為 null。

  System.ArgumentException:tasks 引數包含 null 元素。

5 取消任務

使用System.Threading.CancellationToken和System.Threading.CancellationTokenSource中斷Task的執行。

1)System.Threading.CancellationToken

傳播有關應取消操作的通知

屬性:

  public bool IsCancellationRequested { get; }

方法:

  public void ThrowIfCancellationRequested();

  如果已請求取消此標記,則引發 System.OperationCanceledException。

異常:

  System.OperationCanceledException:該標記已請求取消。

      System.ObjectDisposedException:關聯的System.Threading.CancellationTokenSource已被釋放。

2) System.Threading.CancellationTokenSource

通知 System.Threading.CancellationToken,告知其應被取消

屬性:

  public CancellationToken Token { get; }:獲取與此 System.Threading.CancellationTokenSource 關聯的 System.Threading.CancellationToken。

異常:

  System.ObjectDisposedException:已釋放標記源。

方法:

  public void Cancel():傳達取消請求。

異常:

  System.ObjectDisposedException:此 System.Threading.CancellationTokenSource 已被釋放。

  System.AggregateException:聚合異常包含由相關聯的 System.Threading.CancellationToken 上已註冊的回撥引發的所有異常。

6 任務的返回值

1)Task型別

在第1節中已經介紹了Task。

2)Task<TResult>型別

屬性

定義

說明

public static TaskFactory<TResult> Factory { get; }

提供對用於建立 System.Threading.Tasks.Task<TResult> 例項的工廠方法的訪問。

public TResult Result { get; }

獲取此 System.Threading.Tasks.Task<TResult> 的結果值

方法

  public Task ContinueWith(Action<Task<TResult>> continuationAction)

引數:

  continuationAction:在 System.Threading.Tasks.Task<TResult> 完成時要執行的操作。在執行時,委託將作為一個引數傳遞給完成的任務。

異常:

  System.ObjectDisposedException:System.Threading.Tasks.Task<TResult> 已被釋放。

  System.ArgumentNullException:continuationAction 引數為 null。

注意:

  • 該方法的過載方法提供了更多的控制機制。可以傳入CancellationToken、TaskContinuationOptions、TaskScheduler引數。
  • 使用Task.Factory.StartNew方法,如果傳入的委託無返回值,那麼方法執行的返回結果型別其實是Task<TResult>,通過Task<TResult>型別的Result 屬性可以檢視返回結果。對於串聯的多個任務,若後續的任務要使用上一個任務的結果,那麼Task.Factory.StartNew返回值型別必須是Task<TResult>或var。
  • 返回值可以是自定義型別。

7 TaskCreationOptions (列舉型別)

用途:控制任務建立與執行的行為。

說明

TaskCreationOptions.None

指定應使用預設行為

TaskCreationOptions.PreferFairness

提示 System.Threading.Tasks.TaskScheduler 以一種儘可 能公平的方式安排任務,這意味著較早安排的任務將更可能較早執行,而較晚安排執行的任務將更可能較晚執行

TaskCreationOptions.LongRunning

指定某個任務將是執行時間長、粗粒度的操作。 它會向 System.Threading.Tasks.TaskScheduler 提示,過度訂閱可能是合理的。

TaskCreationOptions.AttachedToParent

指定將任務附加到任務層次結構中的某個父級

TaskCreationOptions.DenyChildAttach

如果嘗試附有子任務到建立的任務,指定 System.InvalidOperationException 將被引發

TaskCreationOptions.HideScheduler

防止環境計劃程式被視為已建立任務的當前計劃程式。 這意味著像 StartNew 或 ContinueWith 建立任務的執行操作將被視為 System.Threading.Tasks.TaskScheduler.Default當前計劃程式

8 任務計劃TaskScheduler

功能:擴充套件任務執行計劃,例如自定義任務計劃程式來實現效能加速。

屬性:

名稱

說明

Current

當前正在執行的任務關聯的 TaskScheduler

Id

TaskScheduler 的唯一 ID

MaximumConcurrencyLevel

指示此 TaskScheduler 能夠支援的最大併發級別

9 串聯多個任務

1)public Task ContinueWith(Action<Task> continuationAction);

引數:

  continuationAction:在 System.Threading.Tasks.Task 完成時要執行的操作。 在執行時,委託將作為一個引數傳遞給完成的任務。

異常:

  System.ObjectDisposedException:建立了 cancellationToken 的 System.Threading.CancellationTokenSource 已經被釋放。

  System.ArgumentNullException:continuationAction 引數為 null。

2)public Task ContinueWith(Action<Task> continuationAction, TaskContinuationOptions continuationOptions);

引數:

  continuationAction:根據在 continuationOptions 中指定的條件執行的操作。 在執行時,委託將作為一個引數傳遞給完成的任務。

  continuationOptions:用於設定計劃延續任務的時間以及延續任務的工作方式的選項。

3)TaskContinuationOptions

enum型別,用於設定計劃延續任務的時間以及延續任務的工作方式的選項。 這包括條件(如 System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled)和執行選項(如

System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously)。

說明

ContinuationOptions.None

指定應使用預設行為。預設情況下,完成前面的任務之後將安排執行延續任務,而不考慮前面任務的最終 System.Threading.Tasks.TaskStatus。

ContinuationOptions.LongRunning

指定某個任務將是執行時間長、粗粒度的操作。 它會向 System.Threading.Tasks.TaskScheduler 提示,過度訂閱可能是合理的。

ContinuationOptions.AttachedToParent

指定將任務附加到任務層次結構中的某個父級。

ContinuationOptions.DenyChildAttach

如果嘗試附有子任務到建立的任務,指定 System.InvalidOperationException 將被引發。

ContinuationOptions.HideScheduler

防止環境計劃程式被視為已建立任務的當前計劃程式。 這意味著像 StartNew 或 ContinueWith 建立任務的執行操作將被視為System.Threading.Tasks.TaskScheduler.Default當前計劃程式。

ContinuationOptions.LazyCancellation

在延續取消的情況下,防止延續的完成直到完成先前的任務

ContinuationOptions.NotOnRanToCompletion

指定不應在延續任務前面的任務已完成執行的情況下安排延續任務。 此選項對多工延續無效

ContinuationOptions.NotOnFaulted

指定不應在延續任務前面的任務引發了未處理異常的情況下安排延續任務。 此選項對多工延續無效

ContinuationOptions.OnlyOnCanceled

指定只應在延續任務前面的任務已取消的情況下才安排延續任務。 此選項對多工延續無效

ContinuationOptions.NotOnCanceled

指定不應在延續任務前面的任務已取消的情況下安排延續任務。 此選項對多工延續無效

ContinuationOptions.OnlyOnFaulted

指定只應在延續任務前面的任務引發了未處理異常的情況下才安排延續任務。 此選項對多工延續無效

ContinuationOptions.OnlyOnRanToCompletion

指定只應在延續任務前面的任務已完成執行的情況下才安排延續任務。 此選項對多工延續無效

ContinuationOptions.ExecuteSynchronously

指定應同步執行延續任務。 指定此選項後,延續任務將在導致前面的任務轉換為其最終狀態的相同執行緒上執行。 如果在建立延續任務時已經完成前面的任務,則延續任務將在建立此延續任務的執行緒上執行。只應同步執行執行時間非常短的延續任務

ContinuationOptions.PreferFairness

提示 System.Threading.Tasks.TaskScheduler 以一種儘可能公平的方式安排任務,這意味著較早安排的任務將更可能較早執行,而較晚安排執行的任務將更可能較晚執行。

注意:

1)可以通過位操作組合使用多個值。

2)使用ContinuationOptions.None意味著不論前面的任務是否被取消,延續任務都會執行。

異常:

  System.ObjectDisposedException:System.Threading.Tasks.Task 已被釋放。

  System.ArgumentNullException:continuationAction 引數為 null。

  System.ArgumentOutOfRangeException:continuationOptions 引數為 System.Threading.Tasks.TaskContinuationOptions 指定無效值。

3)子任務(巢狀任務):在父任務的委託中建立的 System.Threading.Tasks.Task 例項。 子任務包括兩種:附加的子任務與分離的子任務

  • 分離的子任務是不依賴於其父級而執行。
  • 附加的子任務是使用 TaskCreationOptions.AttachedToParent 選項建立的,依賴於其父任務而執行。 對父任務使用TaskCreationOptions.DenyChildAttach來阻止子任務附加到父任務。
  • 一個任務可以建立任意數量的附加的子任務和分離的子任務,這僅受系統資源限制。
  • 不提倡建立附加的子任務,這樣會大大增加程式設計的複雜性。

10 使用模式

1)建立任務

基本形式:

 1 private void CreatTask()
 2 {
 3     //建立並執行任務
 4     Task task = new Task(() =>
 5     {
 6         //具體操作
 7     });
 8     task.Start();
 9 
10     //建立並將任務加入執行計劃,使用StartNew
11     Task.Factory.StartNew(() => {
12         //具體操作
13     });
14 
15     //建立並將任務加入執行計劃,使用Run
16     Task.Run(() =>
17     {
18         //具體操作
19     });
20 
21     //安排任務
22     Task.Factory.StartNew(() =>
23     {
24         //具體操作
25     },TaskCreationOptions.PreferFairness);
26 }    

建立附加的子任務:

 1 private void CreateTask_Parent()
 2 {
 3     //附加子任務
 4     var taskParent = Task.Factory.StartNew(() =>
 5     {
 6         //操作......
 7         var child = Task.Factory.StartNew(() =>
 8         {
 9             //具體操作
10         }, TaskCreationOptions.AttachedToParent);
11     });
12     taskParent.Wait();
13 
14 
15     //阻止附加子任務
16     var taskParentZ = Task.Factory.StartNew(() =>
17     {
18         //操作......
19         var child = Task.Factory.StartNew(() =>
20         {
21             //即使設定TaskCreationOptions.AttachedToParent也無法將其附加到父任務
22             //具體操作
23         }, TaskCreationOptions.AttachedToParent);
24     }, TaskCreationOptions.DenyChildAttach);
25     taskParentZ .Wait();
26 }    

2)取消任務

 1 public static void CancelFromExternal_Task()
 2 {
 3     CancellationTokenSource cts = new CancellationTokenSource();
 4 
 5     //其他操作...
 6 
 7     //計算condition
 8     bool condition = ...;
 9     if (condition) cts.Cancel();
10     //或使用Operation2_Task(cts);
11     Operation1_Task(cts);
12     //其他操作...
13 
14 }
15 
16     //1 使用IsCancellationRequested屬性
17     private static void Operation1_Task(CancellationTokenSource cts)
18     {
19         CancellationToken ct = cts.Token;
20         Task.Factory.StartNew(() => 
21         {
22             //其他操作...
23             //return只對當前子執行緒有效
24             if (ct.IsCancellationRequested)
25             { return; }
26             //其他操作...
27         },ct);     
28     }
29 
30     //2 使用拋異常的方式
31     private static void Operation2_Task(CancellationTokenSource     cts)
32     {
33         CancellationToken ct = cts.Token; 
34         Task.Factory.StartNew(() =>
35         {
36             //其他操作...
37             ct.ThrowIfCancellationRequested();
38             //其他操作...
39         }, ct);
40     }    

3)等待任務完成

 1 private void WaitFunc()
 2 {
 3     Task task = new Task(() => 
 4     {
 5         //具體操作
 6     });
 7     task.Start();
 8     task.Wait();
 9 }
10 
11 private void WaitAllFunc()
12 {
13     Task task1 = Task.Run(() =>
14     {
15         //具體操作 
16     });
17     Task task2 = Task.Run(() =>
18     {
19         //具體操作 
20     });
21     //等待task1與task2,直到它們完成為止
22     Task.WaitAll(task1, task2);
23 
24     //等待task1與task2,如果超過1000毫秒則返回。
25     Task.WaitAll(new Task[] { task1, task2 },1000);
26 }

4)串聯多個任務

 1 private void contactTasks()
 2 {
 3     var t1 = Task.Factory.StartNew(() =>
 4     {
 5         //具體操作1
 6         //return 返回值;
 7     });
 8 
 9     var t2 = t1.ContinueWith((t) =>
10     {
11         //具體操作2
12         //return 返回值;
13     });
14 
15     var t3 = t2.ContinueWith((t) =>
16     {
17         //具體操作3
18     });
19 
20     var t4 = t1.ContinueWith((t) =>
21     {
22         //具體操作4
23     });
24 
25     var t5 = t1.ContinueWith((t) =>
26     {
27         //具體操作5
28     });
29 
30     Task.WaitAll(t3, t4, t5);
31 }

-----------------------------------------------------------------------------------------

時間倉促,水平有限,如有不當之處,歡迎指正。