(突出显示<string>类型的参数是为了清楚:如果我们省略它,则可以推断出它。)
查询包含在AggregateException中的任务的Result属性时,所有未处理的异常都会自动重新抛出。但是,如果您无法查询其Result属性(并且不调用Wait),则任何未处理的异常都会使该过程失败。
任务并行库具有更多功能,特别适合利用多核处理器。我们将在第5部分中继续讨论TPL。
如果目标是.NET Framework的早期版本(4.0之前),则不能使用任务并行库。相反,您必须使用一种较旧的结构来输入线程池:ThreadPool.QueueUserWorkItem和异步委托。两者之间的区别在于异步委托使您可以从线程返回数据。异步委托也将任何异常封送回调用方。
要使用QueueUserWorkItem,只需使用要在池线程上运行的委托调用此方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
static void Main() { ThreadPool.QueueUserWorkItem (Go); ThreadPool.QueueUserWorkItem (Go, 123); Console.ReadLine(); } static void Go ( object data) // data will be null with the first call. { Console.WriteLine ( "Hello from the thread pool! " + data); } Hello from the thread pool! Hello from the thread pool! 123 |
我们的目标方法Go必须接受单个对象参数(以满足WaitCallback委托)。就像使用ParameterizedThreadStart一样,这提供了一种将数据传递给方法的便捷方法。与Task不同,QueueUserWorkItem不会返回对象来帮助您随后管理执行。另外,您必须在目标代码中显式处理异常-未处理的异常将使程序瘫痪。
ThreadPool.QueueUserWorkItem没有提供一种简单的机制来在线程执行完毕后从线程取回返回值。异步委托调用(简称异步委托)解决了这一问题,允许在两个方向上传递任意数量的类型化参数。此外,异步委托上未处理的异常可以方便地在原始线程(或更准确地说是调用EndInvoke的线程)上重新抛出,因此不需要显式处理。
不要将异步委托与异步方法(以Begin或End开头的方法,例如File.BeginRead / File.EndRead)混淆。异步方法在外部遵循类似的协议,但是它们存在是为了解决更难的问题,我们将在C#4.0的第23章“简而言之”中进行描述。
通过异步委托启动工作任务的方法如下:
在下面的示例中,我们使用异步委托调用与主线程并发执行,主线程是一种返回字符串长度的简单方法:
1
2
3
4
5
6
7
8
9
10
|
static void Main() { Func< string , int > method = Work; IAsyncResult cookie = method.BeginInvoke ( "test" , null , null ); // // ... here's where we can do other work in parallel... // int result = method.EndInvoke (cookie); Console.WriteLine ( "String length is: " + result); } |
static int Work (string s) { return s.Length; }