C# 托盘化DFS:对于大型图形,任务爆炸会破坏这个程序吗?

C# 托盘化DFS:对于大型图形,任务爆炸会破坏这个程序吗?,c#,task-parallel-library,task,C#,Task Parallel Library,Task,在下面的DFS实现中,将为给定图形中的每条边创建一个任务。给定的任务只有在其派生的所有子任务完成后才能完成。这会导致大型图形的缩放问题吗? 我该怎么处理呢 //destination node -> source node ie., for each node, identifies the node which led ot the given node ConcurrentDictionary<Node, Node> path = new ConcurrentDiction

在下面的DFS实现中,将为给定图形中的每条边创建一个任务。给定的任务只有在其派生的所有子任务完成后才能完成。这会导致大型图形的缩放问题吗? 我该怎么处理呢

//destination node -> source node ie., for each node, identifies the node which led ot the given node
ConcurrentDictionary<Node, Node> path = new ConcurrentDictionary<Node, Node>();

bool ParallelDFS(Node src, Node dest, CancellationToken cancellationToken)
{
    if(src == dest)
    {
        cancellationToken.Cancel();
        return true;
    }

    //dest maps to src only the first time dest is encountered during traversal
    bool firstvisit = path.TryAdd(dest,src);

    if(!firstvisit)
        return false;

    return dest.neighbors.AsParallel().Any( (node) => ParallelDFS(node, dest, cancellationToken) );


}
//目标节点->源节点,即,对于每个节点,标识指向给定节点的节点
ConcurrentDictionary路径=新建ConcurrentDictionary();
bool ParallelDFS(节点src、节点dest、CancellationToken CancellationToken)
{
如果(src==dest)
{
cancellationToken.Cancel();
返回true;
}
//只有在遍历过程中第一次遇到dest时,dest才会映射到src
bool firstvisit=path.TryAdd(dest,src);
如果(!首次访问)
返回false;
返回dest.neights.aspallel().Any((node)=>ParallelDFS(node,dest,cancellationToken));
}
在DFS算法的实现中,您还发现了哪些其他潜在问题

在下面的DFS实现中,将为给定图形中的每条边创建一个任务

不,不会的。第三方物流中的并行算法通常力求高效,并且不会产生超出需要的
Task
s。PLINQ采用的方法是为每个PLINQ查询为每个CPU核心创建一个
任务
。但是仍然会有很多,因为每次迭代只做很少的工作,这很可能意味着并行化的开销将超过任何收益

我认为更好的方法是生产者-消费者模式的变体:有少量任务处理一个节点对队列。当任务遇到尚未访问的节点时,将其邻居添加到队列中


手动管理这些任务应该相对简单。另一种方法是对队列使用
BlockingCollection
,并使用
Parallel.ForEach()
来处理它。

我认为为每个邻居创建一个新线程效率很低。这一定会增加很多开销。性能与非并行性能相比如何?@MartinCapodici尚未对其进行测试。注意:这是一项任务…不必转换为线程。对我来说,这似乎不是深度优先搜索。看起来是宽度优先。@Aadith好吧,很公平。决定是否创建线程的开销可能比字典查找要大得多(这是非并行处理所需的全部工作)。我觉得这需要减少并行性。也许在每N个深度?@Rob很难对此进行争论……在紧邻目标的节点上,它的行为类似于BFS……但在其他情况下,它只是并行DFS遍历