您的位置:首页 > 其它

Parallel Programming-Paralle.For && ForEach

2015-11-07 23:30 633 查看
本文主要介绍Parallel.For以及Parallel.ForEach。Parallel.For是普通步长为1的for循环的并行代替方案。Parallel.ForEach是以集合为基准进行循环的foreach的并行代替方案。主要以下内容:

使用例子

如何退出并行循环

Break、Stop详细介绍

Partitioner

一、Parallel.For

1.1 使用例子

class ParallelFor
{
private void Action(int index)
{
Console.WriteLine(index);
Thread.Sleep(1000);
}
public void ParallelAction()
{
 Parallel.For(0, 10, (i) => Action(i));
}
}

class Program
{
static void Main(string[] args)
{
var stopwatch = Stopwatch.StartNew();
stopwatch.Start();
new ParallelFor().ParallelAction();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
Console.Read();
}
}


1.2 运行截图



时间有很大的提升。

二、跳出循环:Break vs Stop

串行执行的for可以使用break/Stop关键字直接退出循环。Parallel.For可以通过loopState进行退出或者停止循环。

class ParallelFor
{
private void Action(int index)
{
Console.WriteLine(index);
Thread.Sleep(1000);
}
public void ParallelAction()
{
Parallel.For(0, 10, (i, loopState) =>
{
if (i == 8)
{
  loopState.Break();//loopState.Stop()
}
Action(i);
});
}
}


LoopState是循环体的第一个参数(可选),第一个是i。

三、 Break和Stop的区别

Break执行时,小于当前的index(i)的操作都会保证执行完成,大于当前index的迭代不再开始(不是不再执行)-》已经开始的还是会继续执行。

Break indicates that no iterations after the current iteration should be run. It effectively cancels any additional iterations of the loop. However, it does not stop any iterations that have already begun execution.

进一步说明:当index为8的循环开始执行时,index为1,2,3的循环不一定已经开始了。所以当Break执行后(index为8),会保证没有执行的1,2,3执行完毕

比如上面的例子i==8时,进行break,上面的代码执行截图:



0-7保证执行,8也打印出来了(有可能还会有10,9),是因为index8对应的循环在break之前已经开始了。

Stop执行时,Parallel会用最快的速度结束循环,如果小于当前执行stop的index对应的循环操作还没有开始,则不会执行这些没有开始的。所以Stop比Break能够更加快速的结束迭代。

Calling the Stop method indicates that any iterations of the loop that have not yet started need not be run. It effectively cancels any additional iterations of the loop. However, it does not stop any iterations that have already begun execution.

Calling the Stop method causes the IsStopped property to return true for any iteration of the loop that is still executing. This is particularly useful for long-running iterations, which can check the IsStopped property and exit early if its value is true.

Stop is typically employed in search-based algorithms, where once a result is found, no other iterations need be executed.

Break和Stop不能同时使用,会有异常抛出。

四、Continue功能。

Parallel.For是没有Continue关键字的,简单的使用Return即可完成Continue的功能。

public void ParallelAction()
{
Parallel.For(0, 10, (i) =>
{
if (i == 10)
{
return;
}
Action(i);
});
}


五、ParallelOptions

Parallel.For通用有ParallelOptions这个属性,通过这个可以指定CancellationSourceToken和MaxDegreeOfParallelism(最大并发数)

public void ParallelAction()
{
var cts = new CancellationTokenSource();
var option = new ParallelOptions()
{
CancellationToken = cts.Token,
MaxDegreeOfParallelism = 5
};
Parallel.For(0, 10, option, (i) =>
{
if (cts.Token.IsCancellationRequested)
{
return;
}
Action(i);
});
}


六、Partitioner

本文一直在讲解Parallel.For上面的操作对于Parallel.ForEach都是通用的。但是Parallel.ForEach有个特殊功能。Partitioner。

6.1 例子

class ParallelForEach
{
public void ParallelAction()
{
int[] items = new int[100000];
Parallel.ForEach(Partitioner.Create(0, 100000, 50000), (range) =>
{
Console.WriteLine("IN Parallel");
for (var i = range.Item1; i < range.Item2; i++)
{
items[i] = i;
}
});
}
}


上面代码的把1000000次分成两个50000进行,这两个50000之间是并行执行的,但是在每个50000(range)是串行自己执行的。上面程序会打印出两"IN Parallel"。

6.2说明

为什么要使用Partitioner?使用并行开发有两大开销,一是线程调度,二是调度代理方法,如果循环体本身开销很小,大量使用线程会得不偿失:调度本身的开销大于使用并行带来的性能提升。上面的例子可以看出,使用Partitioner减少了并行的次数,增大了循环体的体积。达到了并行和调度开销的协调。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: