29.5 任务并行
任务并行指的是同时运行一个或多个任务。任务是任务并行库中的重要概念,在某些方面它和创建新的工作线程或ThreadPool工作项比较类似,但任务的抽象级别更高。
Parallel.Invoke方法是实现任务并行的一种简单方式,使用这种方式,可同时运行任意数量的任意语句。只需为每个工作项传入Action委托即可,创建Action委托的最简单方式是使用lambda表达式,lambda表达式可调用指定的方法。
下面的示例演示了一个基本的Invoke()调用,该调用创建并启动并行运行的两个任务。
Parallel.Invoke(()=>DoSomeWork(),()=>DoSomeOtherWork());
注意 Parallel.Invoke实际创建的Task实例数不一定与所提供的委托数相等。TPL可能会使用各种优化算法,特别是当需要调用的委托数量特别大的时候。
与Task类似,但Task<TResult>类可以有返回值。实际上,Task<TResult>从Task继承。任务对象提供了可在任务的整个生存期内从调用线程访问的方法和属性。例如,可以随时访问任务的Status属性,以确定它是否处于如下状态(由TaskStatus枚举表示)中的一个,如表29-6所示。
在创建任务时,需要使用一个用户自定义委托,该委托就是需要执行的任务。委托可以为命名的委托、匿名方法或者lambda表达式。其中,lambda表达式可以包含对命名方法的调用,如下面的示例所示。
//创建一个任务对象,并为它提供一个lambda表达式形式的用户委托
var taskA=new Task(
()=>System.Console.WriteLine(
“我在工作线程,线程id:{0}!”,
Thread.CurrentThread.ManagedThreadId));
//任务开始执行
taskA.Start();
//从主线程输出一条信息
System.Console.WriteLine(
“我在主线程,线程id:{0}!”,
Thread.CurrentThread.ManagedThreadId);
上述代码的运行结果如下:
我在主线程,线程id:1!
我在工作线程,线程id:3!
请按任意键继续……
除此之外,还可以使用TaskFactory工厂类的StartNew方法创建并启动一个任务,TaskFactory将在29.6节学习。
前面的例子都是没有返回值的,接下来演示如何从任务中返回值。对于需要返回值的任务,需要使用Task<TResult>类。在下面的示例代码中,任务要调用的目标方法返回值类型为double,因此任务的类型为Task<double>。每个任务都具有包含计算结果的公共Result属性。任务以异步方式运行,可以按任意顺序完成。如果在计算完成之前访问Result,则该属性将一直处于阻止状态,直到值可用为止,如代码清单29-8所示。
代码清单29-8 从任务中返回值
using System;
using System.Threading.Tasks;
namespace ProgrammingCSharp4
{
class ParallelSample
{
static void Main(string[]args)
{
Task<double>[]taskArray=new Task<double>[]{
Task<double>.Factory.StartNew(()=>DoSomething1()),
Task.Factory.StartNew(()=>DoSomething2()),
Task.Factory.StartNew(()=>DoSomething3())
};
double[]results=new double[taskArray.Length];
for(int i=0;i<taskArray.Length;i++)
{
results[i]=taskArray[i].Result;
}
foreach(double result in results)
{
Console.WriteLine(result);
}
}
private static double DoSomething3()
{
return 1f;
}
private static double DoSomething2()
{
return 2f;
}
private static double DoSomething1()
{
return 3f;
}
}
}
上述代码的运行结果为:
3
2
1
请按任意键继续……