Console.WriteLine("主線程執行其他處理"); 15: //主線程掛起1000毫秒,等待任務的完成。 16: Thread.Sleep(1000); 17: } 任務調度結果: 2.等待任務的完成並獲取返回值 使用任務執行非同步操作時,最主要的是要後的任務完成時的返回值。在任務類中有一 ...
Console.WriteLine("主線程執行其他處理");
15: //主線程掛起1000毫秒,等待任務的完成。
16: Thread.Sleep(1000);
17: }
2.等待任務的完成並獲取返回值
使用任務執行非同步操作時,最主要的是要後的任務完成時的返回值。在任務類中有一個實例方法Wait(有許多重載版本)他能等待任務的完成,我們也可以通過Task類的派生類Task<TResult>創建一個非同步任務,並指定任務完成時返回值的類型,這樣可以通過Task<TResult>的實例對象獲取到任務完成後的返回值。創建一個非同步任務並執行0到100求和操作返回最後的計算結果,示例代碼:
1: static void TaskWait() {
2: //創建任務
3: Task<int> task = new Task<int>(() =>
4: {
5: int sum = 0;
6: Console.WriteLine("使用Task執行非同步操作.");
7: for (int i = 0; i < 100; i++)
8: {
9: sum+=i;
10: }
11: return sum;
12: });
13: //啟動任務,並安排到當前任務隊列線程中執行任務(System.Threading.Tasks.TaskScheduler)
14: task.Start();
15:
16: Console.WriteLine("主線程執行其他處理");
17: //等待任務的完成執行過程。
18: task.Wait();
19: //獲得任務的執行結果
20: Console.WriteLine("任務執行結果:{0}", task.Result.ToString());
21: }
Task類還有一些靜態方法,WaitAll用於等待提供的所有 System.Threading.Tasks.Task 對象完成執行過程和Wait用於等待提供的任一個 System.Threading.Tasks.Task 對象完成執行過程,這兩個方法都有一些重載版本。
//等待所有任務完成
public static void WaitAll(params Task[] tasks);
//等待任意一個任務完成
public static int WaitAny(params Task[] tasks);
3.使用ContinueWith方法在任務完成時啟動一個新任務
在使用能夠Task類的Wait方法等待一個任務時或派生類的Result屬性獲得任務執行結果都有可能阻塞線程,為瞭解決這個問題可以使用ContinueWith方法,他能在一個任務完成時自動啟動一個新的任務來處理執行結果。
示例代碼:
1: static void TaskContinueWith()
2: {
3: //創建一個任務
4: Task<int> task = new Task<int>(() =>
5: {
6: int sum = 0;
7: Console.WriteLine("使用Task執行非同步操作.");
8: for (int i = 0; i < 100; i++)
9: {
10: sum += i;
11: }
12: return sum;
13: });
14: //啟動任務,並安排到當前任務隊列線程中執行任務(System.Threading.Tasks.TaskScheduler)
15: task.Start();
16: Console.WriteLine("主線程執行其他處理");
17: //任務完成時執行處理。
18: Task cwt = task.ContinueWith(t => {
19: Console.WriteLine("任務完成後的執行結果:{0}", t.Result.ToString());
20: });
21: Thread.Sleep(1000);
22: }
上述示例中任務不是等待完成來顯示執行結果,而是使用ContinueWith方法,它能夠知道任務在什麼時候完成並啟動一個新的任務來執行任務完成後的處理。ContinueWith方法具有一些重載版本,這些重載版本允許指定延續任務需要使用的數據、延續任務的工作方式(System.Threading.Tasks.TaskContinuationOptions的枚舉值按位OR運行的結果)等。
4.創建父子任務和任務工廠的使用
通過Task類創建的任務是頂級任務,可以通過使用 TaskCreationOptions.AttachedToParent 標識把這些任務與創建他的任務相關聯,所有子任務全部完成以後父任務才會結束操作。示例如下:
1: static void ParentChildTask() {
2: Task<string[]> parent = new Task<string[]>(state => {
3: Console.WriteLine(state);
4: string[] result=new string[2];
5: //創建並啟動子任務
6: new Task(() => { result[0]= "我是子任務1。"; },TaskCreationOptions.AttachedToParent).Start();
7: new Task(() => { result[1] = "我是子任務2。"; }, TaskCreationOptions.AttachedToParent).Start();
8: return result;
9: },"我是父任務,併在我的處理過程中創建多個子任務,所有子任務完成以後我才會結束執行。");
10: //任務處理完成後執行的操作
11: parent.ContinueWith(t => {
12: Array.ForEach(t.Result, r=>Console.WriteLine(r));
13: });
14: //啟動父任務
15: parent.Start();
16: Console.Read();
17: }
如果需要創建一組具有相同狀態的任務時,可以使用TaskFactory類或TaskFactory<TResult>類。這兩個類創建一組任務時可以指定任務的CancellationToken、TaskCreationOptions、TaskContinuationOptions和TaskScheduler預設值。示例代碼:
1: static void TaskFactoryApply()
2: {
3: Task parent = new Task(() =>
4: {
5: CancellationTokenSource cts = new CancellationTokenSource(5000);
6: //創建任務工廠
7: TaskFactory tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
8: //添加一組具有相同狀態的子任務
9: Task[] task = new Task[]{
10: tf.StartNew(() => { Console.WriteLine("我是任務工廠里的第一個任務。"); }),
11: tf.StartNew(() => { Console.WriteLine("我是任務工廠里的第二個任務。"); }),
12: tf.StartNew(() => { Console.WriteLine("我是任務工廠里的第三個任務。"); })
13: };
14: });
15: parent.Start();
16: Console.Read();
17: }
5.任務內部實現和任務調度
任務內部有一組構成任務狀態的屬性,標識任務的唯一Id、表示任務的執行狀態(TaskStatus)、任務創建時提供的回調函數的引用和傳遞給回調函數的數據對象AsyncState、對任務創建時的任務調度對象(TaskScheduler)的引用、對父任務的引用以及對執行上下文的引用和ManualResetEventSlim對象的引用。Task類和Task<TResult>類都實現了標準的釋放資源的介面,允許在任務完成處理的時候使用Dispose方法釋放資源(關閉ManualResetEventSlim對象實例)。可以使用Task類的CurrentId屬性獲得正在執行的任務的Id,如果沒有任務在執行CurrentId返回值為null,CurrentId是一個int?可空類型的屬性。任務執行的生命周期通過TaskStatus類型的一個值來表示,TaskStatus所包含的值:
public enum TaskStatus
{
Created = 0,
WaitingForActivation = 1,
WaitingToRun = 2,
Running = 3,
WaitingForChildrenToComplete = 4,
RanToCompletion = 5,
Canceled = 6,
Faulted = 7,
}
我們可以通過Task類的Exception屬性獲得任務在執行過程中的所有異常
,Exception是一個AggregateException類型的屬性。Task類提供了IsCanceled、IsCompleted、IsFaulted屬性來獲得任務的完成狀態。通過ContinueWith、ContinueWhenAll、ContinueWhenAny和FromAsync創建的後續任務都處於WaitingForActivation 狀態,這個狀態的任務會在父任務完成後自動執行。
在任務內部由TaskScheduler類調度任務的執行,該類是一個抽象類,FCL中從他派生了兩個派生類:ThreadPoolTaskScheduler線程池任務調度器和SynchronizationContextTaskScheduler同步上下文任務調度器。所有任務預設都是採用ThreadPoolTaskScheduler調度任務,他是採用線程池來執行任務,可以通過TaskScheduler類的靜態屬性Default獲得對預設任務調度器的引用。SynchronizationContextTaskScheduler任務調度器能夠用在Window form、WPF等應用程式,他的任務調度是採用的GUI線程,所以他能同步更新UI組件,可以通過TaskScheduler類的靜態方法FromCurrentSynchronizationContext獲得對一個同步上下文任務調度起的引用。
任務調度示例:
1: private void button1_Click(object sender, EventArgs e)
2: {
3: //獲得同步上下文任務調度器
4: TaskScheduler m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
5:
6: //創建任務,並採用預設任務調度器(線程池任務調度器)執行任務
7: Task<int> task = new Task<int>(() =>
8: {
9: //執行複雜的計算任務。
10: Thread.Sleep(2000);
11: int sum = 0;
12: for (int i = 0; i < 100; i++)
13: {
14: sum += i;
15: }
16: return sum;
17: });
18: var cts=new CancellationTokenSource();
19: //任務完成時啟動一個後續任務,並採用同步上下文任務調度器調度任務更新UI組件。
20: task.ContinueWith(t => {this.label1.Text="採用SynchronizationContextTaskScheduler任務調度器更新UI。\r\n計算結果是:"+task.Result.ToString(); },
21: cts.Token ,TaskContinuationOptions.AttachedToParent,m_syncContextTaskScheduler);
22: task.Start();
23: }
本文簡單的介紹了使用Task類來執行非同步操作以及任務的內部實現與任務調度。在執行複雜非同步操作時,可以採用任務來執行,他能更好的知道非同步操作在何時完成以及返回非同步操作的執行結果。