初识Parallel Extensions之TPL(二)
初识Parallel Extensions之TPL(二)
LazyBee
前面我们谈了结构并行化的For循环(具体参见:初识Parallel Extensions之TPL),今天继续谈谈结构并行化的ForEach和Do。
ForEach
对于IEnumerable<T>的循环,并行扩展也提供了相应的并行实现,就是Parallel..ForEach.下面就让我们来看看,Parallel.ForEach的定义:
public static void ForEach<TSource>(IEnumerable<TSource> source, Action<TSource> body)
其中:source是要被处理的数据源,
body是针对每一个数据源中的元素要执行的操作。
例如:我们需要得到指定目录下所有扩展名为txt的文本文件中包含LazyBee的文件个数:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![](/Images/OutliningIndicators/None.gif)
在有了上面的定义之后,我们就可以写我们的并行循环搜索了:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![](/Images/OutliningIndicators/None.gif)
当然,Parallel.ForEach也提供了和Parallel.For相似的重载版本:
public static void ForEach<TSource>(IEnumerable<TSource> source,
Action<TSource, int, ParallelState> body)
public static void ForEach<TSource, TLocal>(
IEnumerable<TSource> source,
Func<TLocal> threadLocalSelector,
Action<TSource, int, ParallelState<TLocal>> body,
Action<TLocal> threadLocalCleanup
)
其中,source是要被处理的数据源
threadLocalSelector是产生线程本地状态的函数
body是每次循环(迭代)要进行的处理操作。这个操作都有三个输入参数,第一个
就是数据源中的元素,int类型的参数是该元素在数据源中的index.ParallelState类型的参数是用来在需要的情况下来终止循环的,ParallelState<TLocal>是用来保存线程本地状态以及需要时终止循环的。
threadLocalCleanup是用于清理线程本地状态的操作
关于这些重载版本的使用和Parallel.For的使用类似,具体请参考初识Parallel Extensions之TPL的For循环部分的示例。
Do
Parallel.Do主要是用来并行化那些不存在相互依赖顺序的任务,这种情况在递归中是比较多见的,例如遍历树型结构时向左和向右没有顺序之分就可以使用它。具体我们还是来先看看其是如何定义的:
public static void Do(params Action[] actions)
public static void Do(Action[] actions, TaskManager manager,
TaskCreationOptions options)
其中:actions是并行要执行的操作(任务)。这个Action代理类型是.Net3.5引入的,用于定义不带参数也没有返回值的操作。
manager是任务的调度管理器。你可以使用它配合TaskManagerPolicy来指定并行运
行的线程数。
options是一个枚举类型,可以指定以下四种取值:
None:指定使用缺省的行为。
SuppressExecutionContextFlow:指定并不继承当前执行的上下文
RespectParentCancelation:如果父任务取消,该任务也随之取消
SelfReplicating:在处理器可用时将进行自我复制
下面我们来看一个例子,假设我们有一个带有整形数据的二叉树,我们现在需要遍历每一个节点,并且将每个节点的数值都加2,首先我们来定义我们的二叉树:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![](/Images/OutliningIndicators/None.gif)
接下来,是填充二叉树数据的函数:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![](/Images/OutliningIndicators/None.gif)
最后就是如何来实现我们的业务逻辑的代码了:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![](/Images/OutliningIndicators/None.gif)
Parallel.Do只有在语句中的所有任务都完成后才会继续往下走。
大家能看到有了Lambda表达式,我们真是省去了很多的代码。
有关TaskManager以及TaskCreationOptions如何使用将在下一次任务并行性中来描述。