26.6 Parallel的静态For,ForEach和Invoke方法
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ThreadStudy
{
public class Program
{
static void Main(string[] args)
{
Parallel.For(98, 101, i => Console.WriteLine("Sum({0})={1};",i,Sum(i)));
Thread.Sleep(2000);
Console.WriteLine();
int[] arr={100,99,98};
Parallel.ForEach(arr, i => Console.WriteLine("Sum({0})={1};", i, Sum(i)));
Thread.Sleep(2000);
Console.WriteLine();
Parallel.Invoke(() => Console.WriteLine("Sum({0})={1};", 100, Sum(100)),
() => Console.WriteLine("Sum({0})={1};", 99, Sum(99)),
() => Console.WriteLine("Sum({0})={1};", 98, Sum(98)));
Console.ReadKey();
}
public static int Sum(int num)
{
if (num == 0)
return 0;
else
return num + Sum(num - 1);
}
}
}
Parallel的所有方法都让调用线程参与处理。从资源利用的角度说,这是一件好事,因为我们不希望调用线程停下来,等线程池线程做完所有工作才继续。然而,如果调用线程在线程池线程完成自己的那一部分工作之前完成工作,调用线程就会将自己挂起,直到所有工作完成。这也是一件好事,因为这提供了和使用普通for或foreach循环时相同的语义:线程要在所有工作完成后才继续运行。还要注意,如果任何操作抛出一个未处理的异常,你调用的Parallel方法最后会抛出一个AggregateException。
调用Parallel的方法时,有一个前提条件务必牢记:工作项要能并行执行才行。
除此之外,Parallel的方法本身也有开销:委托对象必须分配,而针对每一个工作项,都要调用一次这些委托。
For和ForEach方法有一些重载版本允许传递3个委托:
任务局部初始化委托(localInit),为参与工作的每一个任务都调用一次该委托。这个委托是在任务被要求处理一个工作项之前调用的。
主体委托(body),为参与工作的各个线程所处理的每一项都调用一次该委托。
任务局部终结委托(localFinally),为参与工作的每一个任务都调用一次该委托。这个委托是在任务处理好派遣给它的所有工作之后调用的。即使主体委托代码引发一个未处理的异常,也会调用它。
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ThreadStudy
{
public class Program
{
static void Main(string[] args)
{
Console.WriteLine(Sum());
Console.ReadKey();
}
public static int Sum(int num)
{
if (num == 0)
return 0;
else
return num + Sum(num - 1);
}
private static Int64 Sum()
{
var nums = new int[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Int64 masterTotal = 0;
ParallelLoopResult result = Parallel.ForEach<int, Int64>(nums,
() => { return 0; },
(num, loopstate, index, taskLocalTotal) =>{return taskLocalTotal + num;},
taskLocalTotal => { Interlocked.Add(ref masterTotal, taskLocalTotal); });
return masterTotal;
}
}
}
我们向主体委托传递了一个ParallelLoopState对象。
Parallel的For和ForEach方法都返回一个ParallelLoopResult实例。可通过检查ParrallelLoopResult的属性来了解循环的结果。如果IsCompleted返回true,表明循环运行完成,所有项都得到了处理。