• 初识Parallel Extensions之TPL(二)


     

    初识Parallel ExtensionsTPL(二)

    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的文件个数:

    private static IEnumerable<string> GetFiles(string path,string extName)
    {
        
    if (path == nullthrow new ArgumentNullException("path");
        
    if (File.Exists(path) && path.EndsWith(extName) )
        
    {
            
    yield return path;
        }

        
    else if (Directory.Exists(path))
        
    {
            
    foreach (string file in Directory.GetFiles(path,"*."+extName)) 
    yield return file;
            
    foreach (string directory in Directory.GetDirectories(path))
            
    {
                
    foreach (string file in GetFiles(directory, "*."+extName)) 
    yield return file;
            }

        }

    }

    private static bool FileContainsString(string text, string path)
    {
        
    using (StreamReader sr = new StreamReader(File.OpenRead(path)))
        
    {
            
    string line;
            
    while ((line = sr.ReadLine()) != null)
            
    {
              
    if (line.IndexOf(text, 0, StringComparison.CurrentCultureIgnoreCase) >= 0
                
    return true;
            }

        }

        
    return false;
    }

    在有了上面的定义之后,我们就可以写我们的并行循环搜索了:

    int count = 0;
    Parallel.ForEach(GetFiles(path,
    "txt"), file =>
    {
        
    if (FileContainsString(text, file)) Interlocked.Increment(ref count);
    }
    );

    当然,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,首先我们来定义我们的二叉树:

    class Tree<T>
    {
        
    public T Data;
        
    public Tree<T> Left, Right;
    }

    接下来,是填充二叉树数据的函数:

    private static Random _rnd = new Random();
    public static Tree<int> GenerateRandom(int depth)
    {
        Tree
    <int> root = new Tree<int> { Data = _rnd.Next() };
        
    if (depth > 0)
        
    {
            root.Left 
    = GenerateRandom(depth - 1);
            root.Right 
    = GenerateRandom(depth - 1);
        }

        
    return root;
    }

    最后就是如何来实现我们的业务逻辑的代码了:

    public static void MyParallel()
    {
        Tree
    <int> tree =GenerateRandom(5);
        ProcessTreeParallel(tree, (tmpTree, i) 
    => { tmpTree.Data += i; Console.WriteLine(tmpTree.Data); },2);
    }


    public static void ProcessTreeParallel(Tree<int> tree,Action<Tree<int>,int> action,int v1)
    {
        
    if (tree == nullreturn;
        Parallel.Do(() 
    => ProcessTreeParallel(tree.Left, action),
                     ()
    =>ProcessTreeParallel(tree.Right, action),
                     ()
    =>action(tree,v1));
    }

    Parallel.Do只有在语句中的所有任务都完成后才会继续往下走。
    大家能看到有了
    Lambda表达式,我们真是省去了很多的代码。

    有关TaskManager以及TaskCreationOptions如何使用将在下一次任务并行性中来描述。

  • 相关阅读:
    Range对象
    WorkSheet属性和方法
    Workbooks对象集
    常用对象之Application
    过程(Sub和Function)
    VBA基本语言结构
    Python网络编程-UDP(基于socket)
    Python网络编程-TCP(基于socket)
    二叉树的创建与遍历算法-C语言
    级数收敛的判定步骤
  • 原文地址:https://www.cnblogs.com/LazyBee/p/1128049.html
Copyright © 2020-2023  润新知