我遇到需要以并行方式访问树的情况。 这怎么可能?没有递归,我无法想到任何树木。
答案 0 :(得分:0)
不确定您是否还在寻找答案,但我认为其他人可能会受益。
你仍然可以使用递归,但你需要锁定树的分支,并允许其他线程跳过'那些分支,因为它们已经(或已经被)处理过。
例如,假设您正在处理执行广度优先遍历的树,因此在每次运行递归方法时,您将遍历当前节点的子节点。每个线程必须遍历当前节点的子节点并尝试锁定每个子节点,如果它已被锁定则跳过它。
我不确定您使用的是哪种树实现,因此您应该对待节点'以下示例中的对象代码为伪代码,但其余的是正确的Java。
在下面的示例中,选择预定深度以应用锁定 - 这可确保线程不会简单地锁定树的根并序列化对整个树的访问。
选择正确的深度取决于特定树的形状。例如,如果它是一个二叉树,那么你的树在深度3可能有多达8个分支,这对于几个线程来说很可能是好的,但如果你运行20个线程,你需要选择一个序列化点较低的深度,以确保所有线程都能够处理一些分支。
您还可以使用其他一些方法来决定何时锁定(例如,如果节点可以有效地报告它有多少个叶子节点,您可以在其上设置阈值),重要的是所有线程都使用相同的代码决定锁定或不锁定。
Object master_LOCK = new Object();
HashMap locks = new HashMap();
int DEPTH = 5;
public boolean lockNode(Object nodeID) {
synchronized(master_LOCK) {
Object node_LOCK = (Object)locks.get(nodeID);
if (node_LOCK == null) {
//we can lock this node
locks.put(nodeID,new Object());
return true;
} else {
//node already locked
return false;
}
}
}
public void traverseBreadhFirst(Node node) {
locks.clear();
traverseBreadthFirst(node,0);
}
public void traverseBreadthFirst(Node node, int currentDepth) {
currentDepth++;
//Process the current node here
//Iterate through children, locking to force serialised access only at a predetermined depth
List children = node.getChildren();
for (int i = 0; i < children.size(); i++) {
Node child = (Node)children.get(i);
//If we are a certain depth in the tree we start serialising access from that point on
//This ensures that there are enough branches at this point to roughly evenly distribute them
//to all available threads.
if (currentDepth < DEPTH ) {
//All threads are to go to depth N
traverseBreadthFirst(node,currentDepth);
} else if (currentDepth == DEPTH ) {
//At depth N we lock all branches we intend to process
if (lockNode(child.getID())) {
//We have locked this child node so we should process it
traverseBreadthFirst(node,currentDepth);
} else {
//This child has been locked for processing already so we skip it
}
} else {
//We are deeper than depth N so we can traverse our locked branch without locking further
traverseBreadthFirst(node,currentDepth);
}
}
}