如何使用递归方法实现并行性



我有一个树结构,因为我正在使用此类:

class Node
{
    long IdNode;
    List<Node> Childs;
    string path;
    Data data;
}

路径是由"。"矛盾的iDNode,因此节点的路径为" idparent.idnode"等。因此,要设置节点的路径,我需要父的路径。

我有此方法:

public setPath(Node paramParentNode)
{
    this.path = paramParentNode.Path + "." + this.IDNode;
    foreach(Node iteratorNode in this.Childs)
    {
        iteratorNode.setPath(this);
    }
}

这是一个秘密版本。但是我在考虑如何并行实施,类似的事情:

public setPathMt(Node paramParentNode)
{
    this.path = paramParentNode.Path + "." + this.IDNode;
    Parallel.Foreach(this.Childs,
     (iteratorNode) =>
      {
          iteratorNode.SetPathMt(this);
      }
     );
}

但我不知道这是正确的方法,因为我不知道该如何等待该方法的递归呼叫,我的意思是,我如何知道递归方法何时完成。

这是实现多线程递归方法的最佳方法?

谢谢。

您的方法应该像

public SetPath(Node paramParentNode)
{
    paramParentNode.Path = paramParentNode.Path + "." + this.IDNode;
    foreach(Node iteratorNode in paramParentNode.Childs)
    {
        SetPath(iteratorNode);
    }
}

和像这样的并行方法

public SetPathMt(Node paramParentNode)
{
    paramParentNode.Path = paramParentNode.Path + "." + this.IDNode;
    Parallel.Foreach(paramParentNode.Childs,
     new ParallelOptions { MaxDegreeOfParallelism = 32 },
     (iteratorNode) =>
      {
          SetPathMt(iteratorNode);
      }
     );
}

您根本不使用该方法中传递的节点。当您使用this时,这是指该类的实例,在所有递归方法中都保持不变。唯一改变的是方法中的参数通过。

new ParallelOptions { MaxDegreeOfParallelism = 32 }是限制此并行操作使用的并发操作数(线程)的数量为32(可以是您想要的数字)或-1(所有可用线程)。

不确定为什么要并行运行此操作。我认为您需要一棵大树来获得任何优势。但是,无论哪种方式,以下代码都是一个完整的工作示例,可以在您指定的情况下构建路径:

using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace ClassLibrary2 {
    [TestFixture]
    public class NodePathHandler {
        [Test]
        public void SetNodePathsTest() {
            var tree = new Node() {
                IdNode = 1,
                Childs = Enumerable.Range(1, 2).Select(nId1 => new Node() {
                    IdNode = 1 + nId1,
                    Childs = Enumerable.Range(1, 2).Select(nId2 => new Node() {
                        IdNode = nId1 + nId2,
                        Childs = Enumerable.Range(1, 2).Select(nId3 => new Node() {
                            IdNode = nId2 + nId3,
                            Childs = Enumerable.Empty<Node>().ToList()
                        }).ToList()
                    }).ToList()
                }).ToList()
            };
            var sw = Stopwatch.StartNew();
            SetNodePaths(tree);
            Console.WriteLine($"Time: {sw.ElapsedMilliseconds}ms");
        }
        public void SetNodePaths(Node node, string parentPath = null) {
            node.Path = parentPath == null ? node.IdNode.ToString() : $"{parentPath}.{node.IdNode}";            
            //Sequential
            //node.Childs.ForEach(n => SetNodePaths(n, node.Path));
            //Parallel
            Parallel.ForEach(node.Childs, n => SetNodePaths(n, node.Path));
            Console.WriteLine($"Node Id: {node.IdNode} Node Path: {node.Path}");
        }
    }
    public class Node {
        public long IdNode { get; set; }
        public List<Node> Childs { get; set; }
        public string Path { get; set; }
        public Data Data { get; set; }
    }
    public class Data { }
}

和小样本树的输出为:

Node Id: 2 Node Path: 1.2.2.2
Node Id: 3 Node Path: 1.2.2.3
Node Id: 2 Node Path: 1.2.2
Node Id: 3 Node Path: 1.2.3.3
Node Id: 2 Node Path: 1.3.3.2
Node Id: 3 Node Path: 1.3.4.3
Node Id: 3 Node Path: 1.3.3.3
Node Id: 3 Node Path: 1.3.3
Node Id: 4 Node Path: 1.2.3.4
Node Id: 4 Node Path: 1.3.4.4
Node Id: 4 Node Path: 1.3.4
Node Id: 3 Node Path: 1.2.3
Node Id: 3 Node Path: 1.3
Node Id: 2 Node Path: 1.2
Node Id: 1 Node Path: 1
Time: 4ms

要注意的另一个选项是以Sequential示例为例,并将呼叫添加到AsParallel().ForAll而不是ForEach。通常,这代表比Parallel类更大的开销,但是如果您真的担心性能,那么该变体值得投入混音。

最新更新