多核时代 .NET Framework 4 中的并行编程5---并行循环Parallel Loop
1. 并行循环与顺序循环区别
首先,来运行下面的代码,查看区别,代码如下:
static void Main(string[] args)
{
Console.WriteLine("使用For循环");
for (int i = 0; i <= 10; i++)
{
Console.WriteLine("i = {0}, 线程id = {1}",
i, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(10);
}
Console.WriteLine("使用Parallel.For");
Parallel.For(0, 10, i =>
{
Console.WriteLine("i = {0}, 线程id = {1}", i,
Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(10);
});
Console.ReadLine();
}
最终运行的效果如下图所示:
从上面的代码运行效果图来我们可以看到,只有一个线程在处理for循环.而Parallel.For的循环,则会有多个线程进行处理,从而在某种程度改善了程序代码的运行效率.他们的区别就不言而喻.
2. Parallel的并行循环的几种方法
Parallel类提供以下几种并行执行循环的方法(他们还有很多其他的重载方法)。具体说明如下:
Ø Parallel.For:执行 for 循环,其中可能会并行运行迭代.
Ø Parallel.ForEach: 对某种集合中的对象执行 foreach,其中可能会并行运行迭代,需要主要的是,它不保证按照集合的索引顺序去执行迭代。
Ø Parallel.Invoke:尽可能并行执行提供的每个操作.
示例代码如下:
private static void Invoke()
{
Action[] actions = new Action[5];
actions[0] = new Action(() =>
{
Console.WriteLine("thread id={0}", Thread.CurrentThread.ManagedThreadId);
});
actions[1] = new Action(() =>
{
Console.WriteLine("thread id={0}", Thread.CurrentThread.ManagedThreadId);
});
actions[2] = new Action(() =>
{
Console.WriteLine("thread id={0}", Thread.CurrentThread.ManagedThreadId);
});
actions[3] = new Action(() =>
{
Console.WriteLine("thread id={0}", Thread.CurrentThread.ManagedThreadId);
});
actions[4] = new Action(() =>
{
Console.WriteLine("thread id={0}", Thread.CurrentThread.ManagedThreadId);
});
Parallel.Invoke(actions);
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6 };
Parallel.ForEach(list, i =>
{
Console.WriteLine("i={0},thread id={1}", i, Thread.CurrentThread.ManagedThreadId);
});
Console.ReadLine();
}
3. 返回值
ParallelLoopResult提供执行 Parallel 循环的完成状态。其中, ParallelLoopResult有两个属性:
Ø IsCompleted 获取该循环是否已运行完成(即,该循环的所有迭代均已执行,并且该循环没有收到提前结束的请求)。
Ø LowestBreakIteration 获取从中调用 Break 的最低迭代的索引。
代码如下:
ParallelLoopResult loopResult = Parallel.ForEach(list, (int i, ParallelLoopState state) =>
{
if (i == 5)
{
state.Break();
}
Console.WriteLine("i={0},thread id={1}", i, Thread.CurrentThread.ManagedThreadId);
});
Console.WriteLine("IsCompleted: {0}", loopResult.IsCompleted);
Console.WriteLine("BreakValue: {0}", loopResult.LowestBreakIteration.HasValue);
Console.ReadLine();
4. 控制循环
ParallelOptions类存储用于配置 Parallel 类的方法的操作的选项。其属性有:
Ø CancellationToken 获取或设置与此 ParallelOptions 实例关联的 CancellationToken。
Ø MaxDegreeOfParallelism 获取或设置此 ParallelOptions 实例所允许的最大并行度。
Ø TaskScheduler 获取或设置与此 ParallelOptions 实例关联的 TaskScheduler。 将此属性设置为 null,以指示应使用当前计划程序。
如下代码,通过设置CancellationToken以便可以取消Parallel的循环:
CancellationTokenSource token = new CancellationTokenSource();
Task.Factory.StartNew(() =>
{
Thread.Sleep(5000);
token.Cancel();
Console.WriteLine("Token Cancelled.");
});
ParallelOptions loopOptions = new ParallelOptions()
{
CancellationToken = token.Token,
MaxDegreeOfParallelism = 2
};
try
{
Parallel.For(0, Int64.MaxValue, loopOptions, i =>
{
Console.WriteLine("i={0},thread id={1}", i, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
});
}
catch (OperationCanceledException)
{
Console.WriteLine("Exception...");
}
其中, MaxDegreeOfParallelism设置为2,表示最多可以有2个并行量(可以理解成并行线程数目),如果设置为为 -1,表示对于应该使用的并行量没有上限设置。如果将其设置为1,则效果和单线程一样.
5. 停止/中断循环
通过调用ParallelLoopState实例的Stop方法和Break方法,可以停止和中断当前循环的执行.其中,
Ø Break 告知 Parallel 循环应在系统方便的时候尽早停止执行当前迭代之外的迭代.
Ø Stop 告知 Parallel 循环应在系统方便的时候尽早停止执行。
代码如下:
Parallel.ForEach(list, (int i, ParallelLoopState state) =>
{
if (i == 5)
{
state.Break();
}
Console.WriteLine("i={0},thread id={1}", i, Thread.CurrentThread.ManagedThreadId);
});
其中, ParallelLoopState类的实例由Parallel类自动创建并传递到所执行的方法中.所以我们可以直接使用,而不是需要事前申明并实例一个ParallelLoopState对象.