如何用递归方法实现并行?
How to implement parallelism with a recursive method?
我有一个树结构,为此我正在使用这个 class:
class Node
{
long IdNode;
List<Node> Childs;
string path;
Data data;
}
路径就是用“.”分隔的IdNode,所以一个Node的路径就是"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
时,它表示 class 的实例,它在所有递归方法中都保持不变。唯一改变的是方法中传递的参数。
new ParallelOptions { MaxDegreeOfParallelism = 32 }
是限制这个Parallel操作使用的并发操作数(线程)为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
class 更大的开销,但如果您真的关心性能,那么该变体值得加入。
我有一个树结构,为此我正在使用这个 class:
class Node
{
long IdNode;
List<Node> Childs;
string path;
Data data;
}
路径就是用“.”分隔的IdNode,所以一个Node的路径就是"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
时,它表示 class 的实例,它在所有递归方法中都保持不变。唯一改变的是方法中传递的参数。
new ParallelOptions { MaxDegreeOfParallelism = 32 }
是限制这个Parallel操作使用的并发操作数(线程)为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
class 更大的开销,但如果您真的关心性能,那么该变体值得加入。