.NET 4并行编程入门之Task执行和异常处理

开发
本文将介绍的是Task执行和异常处理,这也是在当前多核环境下的.NET并行开发介绍。

查看本系列其他文章,请参看

[[11782]]

本篇主要讲述等待task执行完成。

本篇的议题如下:

1. 等待Task执行完成

2. Task中的异常处理

首先注意一点:这里提到的"等待"和之前文章提到的"休眠"意思是不一样的:

等待:在等待一个task的时候,这个task还是在运行之中的,"等待"相当于在监听运行的task的执行情况。

休眠:让tasku不运行。

在上篇文章中介绍了如果从Task中获取执行后的结果:在Task执行完成之后调用Task.Result获取。其实也可以用其他的方法等待Task执行完成而不获取结果,这是很有用的:如果你想等待一个task完成之后再去做其他的事情。而且我们还可以等待一个task执行完成,或者等待所有的task执行完成,或者等待很多task中的一个执行完成。因为Task是由内部的Scheduler管理的,调用wait方法,其实就是我们在监控task的执行,看看这个task是否执行完了,如果完成,那么wanit方法就返回true,反之。

1. 等待Task执行完成

1.1等待单独的一个Task执行完成

我们可以用Wait()方法来一直等待一个Task执行完成。当task执行完成,或者被cancel,或者抛出异常,这个方法才会返回。可以使用Wait()方法的不同重载。举个例子:

代码

  1. static void Main(string[] args)  
  2.     {  
  3.         // create the cancellation token source  
  4.         CancellationTokenSource tokenSource = new CancellationTokenSource();  
  5.         // create the cancellation token  
  6.         CancellationToken token = tokenSource.Token;  
  7.         // create and start the first task, which we will let run fully  
  8.         Task task = createTask(token);  
  9.         task.Start();  
  10.  
  11.         // wait for the task  
  12.         Console.WriteLine("Waiting for task to complete.");  
  13.         task.Wait();  
  14.         Console.WriteLine("Task Completed.");  
  15.  
  16.         // create and start another task  
  17.         task = createTask(token);  
  18.         task.Start();  
  19.         Console.WriteLine("Waiting 2 secs for task to complete.");  
  20.         bool completed = task.Wait(2000);  
  21.         Console.WriteLine("Wait ended - task completed: {0}", completed);  
  22.  
  23.         // create and start another task  
  24.         task = createTask(token);  
  25.         task.Start();  
  26.         Console.WriteLine("Waiting 2 secs for task to complete.");  
  27.         completed = task.Wait(2000, token);  
  28.         Console.WriteLine("Wait ended - task completed: {0} task cancelled {1}",  
  29.         completed, task.IsCanceled);  
  30.  
  31.         // wait for input before exiting  
  32.         Console.WriteLine("Main method complete. Press enter to finish.");  
  33.         Console.ReadLine();  
  34.     }  
  35.  
  36.     static Task createTask(CancellationToken token)  
  37.     {  
  38.         return new Task(() =>  
  39.         {  
  40.             for (int i = 0; i < 5; i++)  
  41.             {  
  42.                 // check for task cancellation  
  43.                 token.ThrowIfCancellationRequested();  
  44.                 // print out a message  
  45.                 Console.WriteLine("Task - Int value {0}", i);  
  46.                 // put the task to sleep for 1 second  
  47.                 token.WaitHandle.WaitOne(1000);  
  48.             }  
  49.         }, token);  
  50.     } 

从上面的例子可以看出,wait方法子task执行完成之后会返回true。

注意:当在执行的task内部抛出了异常之后,这个异常在调用wait方法时会被再次抛出。后面再"异常处理篇"会讲述。

1.2.等待多个task

我们也可以用WaitAll()方法来一直到等待多个task执行完成。只有当所有的task执行完成,或者被cancel,或者抛出异常,这个方法才会返回。WiatAll()方法和Wait()方法一样有一些重载。

注意:如果在等在的多个task之中,有一个task抛出了异常,那么调用WaitAll()方法时就会抛出异常。

代码

  1. static void Main(string[] args)  
  2.     {  
  3.         // create the cancellation token source  
  4.         CancellationTokenSource tokenSource = new CancellationTokenSource();  
  5.         // create the cancellation token  
  6.         CancellationToken token = tokenSource.Token;  
  7.         // create the tasks  
  8.         Task task1 = new Task(() =>  
  9.         {  
  10.             for (int i = 0; i < 5; i++)  
  11.             {  
  12.                 // check for task cancellation  
  13.                 token.ThrowIfCancellationRequested();  
  14.                 // print out a message  
  15.                 Console.WriteLine("Task 1 - Int value {0}", i);  
  16.                 // put the task to sleep for 1 second  
  17.                 token.WaitHandle.WaitOne(1000);  
  18.             }  
  19.             Console.WriteLine("Task 1 complete");  
  20.         }, token);  
  21.         Task task2 = new Task(() =>  
  22.         {  
  23.             Console.WriteLine("Task 2 complete");  
  24.         }, token);  
  25.  
  26.         // start the tasks  
  27.         task1.Start();  
  28.         task2.Start();  
  29.         // wait for the tasks  
  30.         Console.WriteLine("Waiting for tasks to complete.");  
  31.         Task.WaitAll(task1, task2);  
  32.         Console.WriteLine("Tasks Completed.");  
  33.         // wait for input before exiting  
  34.         Console.WriteLine("Main method complete. Press enter to finish.");  
  35.         Console.ReadLine();  
  36.     } 

 在上面的例子中,首先创建了两个task,注意我们创建的是可以被cancel的task,因为使用CancellationToken。而且在***个task中还是用waitOne()休眠方法,其实目的很简单:使得这个task的运行时间长一点而已。之后我们就调用了WaitAll()方法,这个方法一直到两个task执行完成之后才会返回的。

1.3.等待多个task中的一个task执行完成

可以用WaitAny()方法来等待多个task中的一个task执行完成。通俗的讲就是:有很多的task在运行,调用了WaitAny()方法之后,只要那些运行的task其中有一个运行完成了,那么WaitAny()就返回了。

代码

  1. static void Main(string[] args)  
  2.     {  
  3.         // create the cancellation token source  
  4.         CancellationTokenSource tokenSource = new CancellationTokenSource();  
  5.         // create the cancellation token  
  6.         CancellationToken token = tokenSource.Token;  
  7.         // create the tasks  
  8.         Task task1 = new Task(() =>  
  9.         {  
  10.             for (int i = 0; i < 5; i++)  
  11.             {  
  12.                 // check for task cancellation  
  13.                 token.ThrowIfCancellationRequested();  
  14.                 // print out a message  
  15.                 Console.WriteLine("Task 1 - Int value {0}", i);  
  16.                 // put the task to sleep for 1 second  
  17.                 token.WaitHandle.WaitOne(1000);  
  18.             }  
  19.             Console.WriteLine("Task 1 complete");  
  20.         }, token);  
  21.         Task task2 = new Task(() =>  
  22.         {  
  23.             Console.WriteLine("Task 2 complete");  
  24.         }, token);  
  25.  
  26.         // start the tasks  
  27.         task1.Start();  
  28.         task2.Start();  
  29.         // wait for the tasks  
  30.         Console.WriteLine("Waiting for tasks to complete.");  
  31.         Task.WaitAll(task1, task2);  
  32.         Console.WriteLine("Tasks Completed.");  
  33.         // wait for input before exiting  
  34.         Console.WriteLine("Main method complete. Press enter to finish.");  
  35.         Console.ReadLine();  
  36.     } 

2. Task中的异常处理

在并行编程(TPL)中另外一个已经标准化了的操作就是"异常处理"。而且在并行编程中异常处理显得尤为重要,因为并行编程时与系统中的线程相关的,出了异常,你开发的程序就很有可能崩溃。

下面就详细介绍TPL中异常处理操作。

 a.处理基本的异常。

 在操作task的时候,只要出现了异常,.NET Framework就会把这些异常记录下来。例如在执行Task.Wait(),Task.WaitAll(),Task.WaitAny(),Task.Result.不管那里出现了异常,***抛出的就是一个System.AggregateException.

 System.AggregateException时用来包装一个或者多个异常的,这个类时很有用的,特别是在调用Task.WaitAll()方法时。因为在Task.WaitAll()是等待多个task执行完成,如果有任意task执行出了异常,那么这个异常就会被记录在System.AggregateException中,不同的task可能抛出的异常不同,但是这些异常都会被记录下来。

下面就是给出一个例子:在例子中,创建了两个task,它们都抛出异常。然后主线程开始运行task,并且调用WaitAll()方法,然后就捕获抛出的System.AggregateException,显示详细信息。

代码

  1. static void Main(string[] args)  
  2.     {  
  3.  
  4.         // create the tasks  
  5.         Task task1 = new Task(() =>  
  6.         {  
  7.             ArgumentOutOfRangeException exception = new ArgumentOutOfRangeException();  
  8.             exception.Source = "task1";  
  9.             throw exception;  
  10.         });  
  11.         Task task2 = new Task(() =>  
  12.         {  
  13.             throw new NullReferenceException();  
  14.         });  
  15.         Task task3 = new Task(() =>  
  16.         {  
  17.             Console.WriteLine("Hello from Task 3");  
  18.         });  
  19.         // start the tasks  
  20.         task1.Start(); task2.Start(); task3.Start();  
  21.         // wait for all of the tasks to complete  
  22.         // and wrap the method in a try...catch block  
  23.         try 
  24.         {  
  25.             Task.WaitAll(task1, task2, task3);  
  26.         }  
  27.         catch (AggregateException ex)  
  28.         {  
  29.             // enumerate the exceptions that have been aggregated  
  30.             foreach (Exception inner in ex.InnerExceptions)  
  31.             {  
  32.                 Console.WriteLine("Exception type {0} from {1}",  
  33.                 inner.GetType(), inner.Source);  
  34.             }  
  35.         }  
  36.         // wait for input before exiting  
  37.         Console.WriteLine("Main method complete. Press enter to finish.");  
  38.         Console.ReadLine();  
  39.     } 

 

 从上面的例子可以看出,为了获得被包装起来的异常,需要调用System.AggregateException的InnerExceptions属性,这个属性返回一个异常的集合,然后就可以遍历这个集合。

而且从上面的例子可以看到:Exeception.Source属性被用来指明task1的异常时ArgumentOutRangeException.

 b.使用迭代的异常处理Handler

 一般情况下,我们需要区分哪些异常需要处理,而哪些异常需要继续往上传递。AggregateException类提供了一个Handle()方法,我们可以用这个方法来处理

AggregateException中的每一个异常。在这个Handle()方法中,返回true就表明,这个异常我们已经处理了,不用抛出,反之。

 在下面的例子中,抛出了一个OperationCancelException,在之前的task的取消一文中,已经提到过:当在task中抛出这个异常的时候,实际上就是这个task发送了取消的请求。下面的代码中,描述了如果在AggregateException.Handle()中处理不同的异常。

代码

  1. static void Main(string[] args)  
  2.     {  
  3.         // create the cancellation token source and the token  
  4.         CancellationTokenSource tokenSource = new CancellationTokenSource();  
  5.         CancellationToken token = tokenSource.Token;  
  6.         // create a task that waits on the cancellation token  
  7.         Task task1 = new Task(() =>  
  8.         {  
  9.             // wait forever or until the token is cancelled  
  10.             token.WaitHandle.WaitOne(-1);  
  11.             // throw an exception to acknowledge the cancellation  
  12.             throw new OperationCanceledException(token);  
  13.         }, token);  
  14.         // create a task that throws an exception  
  15.         Task task2 = new Task(() =>  
  16.         {  
  17.             throw new NullReferenceException();  
  18.         });  
  19.         // start the tasks  
  20.         task1.Start(); task2.Start();  
  21.         // cancel the token  
  22.         tokenSource.Cancel();  
  23.         // wait on the tasks and catch any exceptions  
  24.         try 
  25.         {  
  26.             Task.WaitAll(task1, task2);  
  27.         }  
  28.         catch (AggregateException ex)  
  29.         {  
  30.             // iterate through the inner exceptions using  
  31.             // the handle method  
  32.             ex.Handle((inner) =>  
  33.             {  
  34.                 if (inner is OperationCanceledException)  
  35.                 {  
  36.  
  37.                     // ...handle task cancellation...  
  38.                     return true;  
  39.                 }  
  40.                 else 
  41.                 {  
  42.                     // this is an exception we don't know how  
  43.                     // to handle, so return false  
  44.                     return false;  
  45.                 }  
  46.             });  
  47.         }  
  48.         // wait for input before exiting  
  49.         Console.WriteLine("Main method complete. Press enter to finish.");  
  50.         Console.ReadLine();  
  51.     } 

 今天就写到这里。谢谢!

原文标题:.NET 4 并行(多核)编程系列之五

链接:http://www.cnblogs.com/Leo_wl/archive/2010/06/01/1749598.html

【编辑推荐】

  1. 微软发布新版Windows 7及.NET 4软件开发工具包
  2. 详解.NET 4.0并行计算支持历史
  3. 详读.NET 4.0环境配置
  4. 详解.NET 4.0中异常处理方面的新特性
  5. 三方面诠释.NET 4.0的新特性
责任编辑:彭凡 来源: 博客园
相关推荐

2010-06-04 09:11:10

.NET并行编程

2010-06-08 08:41:08

.NET 4并行编程

2010-06-07 08:43:46

.NET 4并行编程

2010-06-02 08:53:51

.NET 4并行编程

2010-06-11 09:01:02

.NET 4并行编程

2011-03-24 09:23:43

.NET 4多核并行

2024-09-29 16:22:18

多线程TaskC#

2024-09-27 19:42:09

工具C#Task​

2013-08-22 17:10:09

.Net异常处理

2010-06-24 09:12:27

.NET 4并行编程

2010-09-06 08:43:13

.NET 4

2010-01-13 13:20:08

VB.NET结构化异常

2014-07-15 10:31:07

asyncawait

2015-10-13 09:18:00

.Net编程教程

2010-04-09 14:48:41

Oracle数据库

2011-07-05 10:20:38

java

2021-02-25 08:40:19

Java异常分类异常防护

2023-12-11 14:19:00

Java程序异常

2009-02-06 14:11:36

ASP.NET.NET全局异常处理

2009-01-05 09:14:17

.NETcatch性能损失
点赞
收藏

51CTO技术栈公众号