.NET 4并行編程入門之Task執(zhí)行和異常處理
查看本系列其他文章,請參看
本篇主要講述等待task執(zhí)行完成。
本篇的議題如下:
1. 等待Task執(zhí)行完成
2. Task中的異常處理
首先注意一點:這里提到的"等待"和之前文章提到的"休眠"意思是不一樣的:
等待:在等待一個task的時候,這個task還是在運行之中的,"等待"相當(dāng)于在監(jiān)聽運行的task的執(zhí)行情況。
休眠:讓tasku不運行。
在上篇文章中介紹了如果從Task中獲取執(zhí)行后的結(jié)果:在Task執(zhí)行完成之后調(diào)用Task.Result獲取。其實也可以用其他的方法等待Task執(zhí)行完成而不獲取結(jié)果,這是很有用的:如果你想等待一個task完成之后再去做其他的事情。而且我們還可以等待一個task執(zhí)行完成,或者等待所有的task執(zhí)行完成,或者等待很多task中的一個執(zhí)行完成。因為Task是由內(nèi)部的Scheduler管理的,調(diào)用wait方法,其實就是我們在監(jiān)控task的執(zhí)行,看看這個task是否執(zhí)行完了,如果完成,那么wanit方法就返回true,反之。
1. 等待Task執(zhí)行完成
1.1等待單獨的一個Task執(zhí)行完成
我們可以用Wait()方法來一直等待一個Task執(zhí)行完成。當(dāng)task執(zhí)行完成,或者被cancel,或者拋出異常,這個方法才會返回??梢允褂肳ait()方法的不同重載。舉個例子:
代碼
- static void Main(string[] args)
- {
- // create the cancellation token source
- CancellationTokenSource tokenSource = new CancellationTokenSource();
- // create the cancellation token
- CancellationToken token = tokenSource.Token;
- // create and start the first task, which we will let run fully
- Task task = createTask(token);
- task.Start();
- // wait for the task
- Console.WriteLine("Waiting for task to complete.");
- task.Wait();
- Console.WriteLine("Task Completed.");
- // create and start another task
- task = createTask(token);
- task.Start();
- Console.WriteLine("Waiting 2 secs for task to complete.");
- bool completed = task.Wait(2000);
- Console.WriteLine("Wait ended - task completed: {0}", completed);
- // create and start another task
- task = createTask(token);
- task.Start();
- Console.WriteLine("Waiting 2 secs for task to complete.");
- completed = task.Wait(2000, token);
- Console.WriteLine("Wait ended - task completed: {0} task cancelled {1}",
- completed, task.IsCanceled);
- // wait for input before exiting
- Console.WriteLine("Main method complete. Press enter to finish.");
- Console.ReadLine();
- }
- static Task createTask(CancellationToken token)
- {
- return new Task(() =>
- {
- for (int i = 0; i < 5; i++)
- {
- // check for task cancellation
- token.ThrowIfCancellationRequested();
- // print out a message
- Console.WriteLine("Task - Int value {0}", i);
- // put the task to sleep for 1 second
- token.WaitHandle.WaitOne(1000);
- }
- }, token);
- }
從上面的例子可以看出,wait方法子task執(zhí)行完成之后會返回true。
注意:當(dāng)在執(zhí)行的task內(nèi)部拋出了異常之后,這個異常在調(diào)用wait方法時會被再次拋出。后面再"異常處理篇"會講述。
1.2.等待多個task
我們也可以用WaitAll()方法來一直到等待多個task執(zhí)行完成。只有當(dāng)所有的task執(zhí)行完成,或者被cancel,或者拋出異常,這個方法才會返回。WiatAll()方法和Wait()方法一樣有一些重載。
注意:如果在等在的多個task之中,有一個task拋出了異常,那么調(diào)用WaitAll()方法時就會拋出異常。
代碼
- static void Main(string[] args)
- {
- // create the cancellation token source
- CancellationTokenSource tokenSource = new CancellationTokenSource();
- // create the cancellation token
- CancellationToken token = tokenSource.Token;
- // create the tasks
- Task task1 = new Task(() =>
- {
- for (int i = 0; i < 5; i++)
- {
- // check for task cancellation
- token.ThrowIfCancellationRequested();
- // print out a message
- Console.WriteLine("Task 1 - Int value {0}", i);
- // put the task to sleep for 1 second
- token.WaitHandle.WaitOne(1000);
- }
- Console.WriteLine("Task 1 complete");
- }, token);
- Task task2 = new Task(() =>
- {
- Console.WriteLine("Task 2 complete");
- }, token);
- // start the tasks
- task1.Start();
- task2.Start();
- // wait for the tasks
- Console.WriteLine("Waiting for tasks to complete.");
- Task.WaitAll(task1, task2);
- Console.WriteLine("Tasks Completed.");
- // wait for input before exiting
- Console.WriteLine("Main method complete. Press enter to finish.");
- Console.ReadLine();
- }
在上面的例子中,首先創(chuàng)建了兩個task,注意我們創(chuàng)建的是可以被cancel的task,因為使用CancellationToken。而且在***個task中還是用waitOne()休眠方法,其實目的很簡單:使得這個task的運行時間長一點而已。之后我們就調(diào)用了WaitAll()方法,這個方法一直到兩個task執(zhí)行完成之后才會返回的。
1.3.等待多個task中的一個task執(zhí)行完成
可以用WaitAny()方法來等待多個task中的一個task執(zhí)行完成。通俗的講就是:有很多的task在運行,調(diào)用了WaitAny()方法之后,只要那些運行的task其中有一個運行完成了,那么WaitAny()就返回了。
代碼
- static void Main(string[] args)
- {
- // create the cancellation token source
- CancellationTokenSource tokenSource = new CancellationTokenSource();
- // create the cancellation token
- CancellationToken token = tokenSource.Token;
- // create the tasks
- Task task1 = new Task(() =>
- {
- for (int i = 0; i < 5; i++)
- {
- // check for task cancellation
- token.ThrowIfCancellationRequested();
- // print out a message
- Console.WriteLine("Task 1 - Int value {0}", i);
- // put the task to sleep for 1 second
- token.WaitHandle.WaitOne(1000);
- }
- Console.WriteLine("Task 1 complete");
- }, token);
- Task task2 = new Task(() =>
- {
- Console.WriteLine("Task 2 complete");
- }, token);
- // start the tasks
- task1.Start();
- task2.Start();
- // wait for the tasks
- Console.WriteLine("Waiting for tasks to complete.");
- Task.WaitAll(task1, task2);
- Console.WriteLine("Tasks Completed.");
- // wait for input before exiting
- Console.WriteLine("Main method complete. Press enter to finish.");
- Console.ReadLine();
- }
2. Task中的異常處理
在并行編程(TPL)中另外一個已經(jīng)標(biāo)準(zhǔn)化了的操作就是"異常處理"。而且在并行編程中異常處理顯得尤為重要,因為并行編程時與系統(tǒng)中的線程相關(guān)的,出了異常,你開發(fā)的程序就很有可能崩潰。
下面就詳細(xì)介紹TPL中異常處理操作。
a.處理基本的異常。
在操作task的時候,只要出現(xiàn)了異常,.NET Framework就會把這些異常記錄下來。例如在執(zhí)行Task.Wait(),Task.WaitAll(),Task.WaitAny(),Task.Result.不管那里出現(xiàn)了異常,***拋出的就是一個System.AggregateException.
System.AggregateException時用來包裝一個或者多個異常的,這個類時很有用的,特別是在調(diào)用Task.WaitAll()方法時。因為在Task.WaitAll()是等待多個task執(zhí)行完成,如果有任意task執(zhí)行出了異常,那么這個異常就會被記錄在System.AggregateException中,不同的task可能拋出的異常不同,但是這些異常都會被記錄下來。
下面就是給出一個例子:在例子中,創(chuàng)建了兩個task,它們都拋出異常。然后主線程開始運行task,并且調(diào)用WaitAll()方法,然后就捕獲拋出的System.AggregateException,顯示詳細(xì)信息。
代碼
- static void Main(string[] args)
- {
- // create the tasks
- Task task1 = new Task(() =>
- {
- ArgumentOutOfRangeException exception = new ArgumentOutOfRangeException();
- exception.Source = "task1";
- throw exception;
- });
- Task task2 = new Task(() =>
- {
- throw new NullReferenceException();
- });
- Task task3 = new Task(() =>
- {
- Console.WriteLine("Hello from Task 3");
- });
- // start the tasks
- task1.Start(); task2.Start(); task3.Start();
- // wait for all of the tasks to complete
- // and wrap the method in a try...catch block
- try
- {
- Task.WaitAll(task1, task2, task3);
- }
- catch (AggregateException ex)
- {
- // enumerate the exceptions that have been aggregated
- foreach (Exception inner in ex.InnerExceptions)
- {
- Console.WriteLine("Exception type {0} from {1}",
- inner.GetType(), inner.Source);
- }
- }
- // wait for input before exiting
- Console.WriteLine("Main method complete. Press enter to finish.");
- Console.ReadLine();
- }
從上面的例子可以看出,為了獲得被包裝起來的異常,需要調(diào)用System.AggregateException的InnerExceptions屬性,這個屬性返回一個異常的集合,然后就可以遍歷這個集合。
而且從上面的例子可以看到:Exeception.Source屬性被用來指明task1的異常時ArgumentOutRangeException.
b.使用迭代的異常處理Handler
一般情況下,我們需要區(qū)分哪些異常需要處理,而哪些異常需要繼續(xù)往上傳遞。AggregateException類提供了一個Handle()方法,我們可以用這個方法來處理
AggregateException中的每一個異常。在這個Handle()方法中,返回true就表明,這個異常我們已經(jīng)處理了,不用拋出,反之。
在下面的例子中,拋出了一個OperationCancelException,在之前的task的取消一文中,已經(jīng)提到過:當(dāng)在task中拋出這個異常的時候,實際上就是這個task發(fā)送了取消的請求。下面的代碼中,描述了如果在AggregateException.Handle()中處理不同的異常。
代碼
- static void Main(string[] args)
- {
- // create the cancellation token source and the token
- CancellationTokenSource tokenSource = new CancellationTokenSource();
- CancellationToken token = tokenSource.Token;
- // create a task that waits on the cancellation token
- Task task1 = new Task(() =>
- {
- // wait forever or until the token is cancelled
- token.WaitHandle.WaitOne(-1);
- // throw an exception to acknowledge the cancellation
- throw new OperationCanceledException(token);
- }, token);
- // create a task that throws an exception
- Task task2 = new Task(() =>
- {
- throw new NullReferenceException();
- });
- // start the tasks
- task1.Start(); task2.Start();
- // cancel the token
- tokenSource.Cancel();
- // wait on the tasks and catch any exceptions
- try
- {
- Task.WaitAll(task1, task2);
- }
- catch (AggregateException ex)
- {
- // iterate through the inner exceptions using
- // the handle method
- ex.Handle((inner) =>
- {
- if (inner is OperationCanceledException)
- {
- // ...handle task cancellation...
- return true;
- }
- else
- {
- // this is an exception we don't know how
- // to handle, so return false
- return false;
- }
- });
- }
- // wait for input before exiting
- Console.WriteLine("Main method complete. Press enter to finish.");
- Console.ReadLine();
- }
今天就寫到這里。謝謝!
原文標(biāo)題:.NET 4 并行(多核)編程系列之五
鏈接:http://www.cnblogs.com/Leo_wl/archive/2010/06/01/1749598.html
【編輯推薦】