并行BFS高峰时间解算器

时间:2016-01-05 21:57:30

标签: c# multithreading parallel-processing parallel.foreach breadth-first-search

因此,对于uni,我们必须执行此分配,我们必须将高峰时间求解器的串行实现并行。求解器使用bfs实现。

以下是默认bfs实现的一部分:

// Initialize empty queue
        Queue<Tuple<byte[], Solution>> q = new Queue<Tuple<byte[], Solution>>();

        // By default, the solution is "no solution"
        foundSolution = new NoSolution();

        // Place the starting position in the queue
        q.Enqueue(Tuple.Create(vehicleStartPos, (Solution)new EmptySolution()));
        AddNode(vehicleStartPos);

        // Do BFS
        while (q.Count > 0)
        {
            Tuple<byte[], Solution> currentState = q.Dequeue();

            // Generate sucessors, and push them on to the queue if they haven't been seen before
            foreach (Tuple<byte[], Solution> next in Sucessors(currentState))
            {
                // Did we reach the goal?
                if (next.Item1[targetVehicle] == goal)
                {
                    q.Clear();
                    foundSolution = next.Item2;
                    break;
                }

                // If we haven't seen this node before, add it to the Trie and Queue to be expanded
                if(!AddNode(next.Item1))
                    q.Enqueue(next);
            }
        }

        Console.WriteLine(foundSolution);
        Console.ReadLine();

我设法把它变成这样的并行:

ConcurrentQueue<Tuple<byte[], Solution>> q = new ConcurrentQueue<Tuple<byte[], Solution>>();

    foundSolution = new NoSolution();

    q.Enqueue(Tuple.Create(vehicleStartPos, (Solution)new EmptySolution()));
    AddNode(vehicleStartPos);

    while (q.Count > 0 && !solutionFound)
    {
        Tuple<byte[], Solution> currentState;
        q.TryDequeue(out currentState);

        Parallel.ForEach(Sucessors(currentState), (next) =>
        {
            // Did we reach the goal?
            if (next.Item1[targetVehicle] == goal)
            {
                solutionFound = true;
                foundSolution = next.Item2;
                return;
            }

            // If we haven't seen this node before, add it to the Trie and Queue to be expanded
            if (!AddNode(next.Item1))
                q.Enqueue(next);
        });
    }

如您所见,我尝试使用concurrentQueue实现并行foreach循环。我感觉concurrentQueue运行良好,但它会自动锁定,因此花费太多时间,使得这种并行实现方式比串行实现慢。

我正在考虑拥有一个无等待或至少无锁的队列,所以我可以节省一点时间,但我不知道如何实现这样的事情。你们能否深入了解这是否可行以及是否比使用常规队列更快?或者可以使用不同的并发数据结构来更好地适应这种情况。不确定ConcurrentBag之类的效果如何。你能否对此有所了解?

此外,在搜索并行bfs实现后,我找不到任何。对于像我这样想要并行实现bfs的人来说,有哪些一般提示和提示?对于队列有什么好的替代方案,使其成为线程安全的?

EDIT1: 我设法实现了这样的任务:

int taskNumbers = Environment.ProcessorCount;
    Task[] tasks = new Task[taskNumbers];

    // Set up the cancellation token
    ctSource = new CancellationTokenSource();

    for (int i = 0; i < taskNumbers; i++)
        tasks[i] = new Task(() =>
        {
            try{    Traverse(); }
            catch{ }
        }, 
        ctSource.Token);

    for (int i = 0; i < taskNumbers; i++)
        tasks[i].Start();

    Task.WaitAll(tasks);

    ctSource.Dispose();

他们调用一个遍历方法,如下所示:

 private static void Traverse()
{
    ctSource.Token.ThrowIfCancellationRequested();

    while (q.Count > 0)
    {
        Tuple<byte[], Solution> currentState;

        if (q.TryDequeue(out currentState))
        {
            foreach (Tuple<byte[], Solution> next in Sucessors(currentState))
            {
                // Did we reach the goal?
                if (next.Item1[targetVehicle] == goal)
                {
                    ctSource.Cancel();
                    foundSolution = next.Item2;
                    return;
                }

                // If we haven't seen this node before, add it to the Trie and Queue to be expanded
                if (!AddNode(next.Item1))
                    q.Enqueue(next);
            }
        }

        if (ctSource.IsCancellationRequested)
            ctSource.Token.ThrowIfCancellationRequested();
    }
}

然而,我在查找遍历方法中while循环的条件时遇到了麻烦。当前条件允许任务过早退出循环。据我所知,我没有可用的所有节点的完整列表,所以我不能将访问的树与所有节点的列表进行比较。除此之外,我没有任何其他的想法,我可以保持任务循环通过while循环,直到我找到答案或直到没有更多的新节点。你能帮助我吗?

到目前为止,Thnx @Brian Malehorn为您提供了帮助,我设法使并行bfs版本的性能几乎与串行版本的性能相当。我现在需要的是让我认为任务保持在while循环中。

1 个答案:

答案 0 :(得分:0)

问题不在于你的队列,问题是你正在并行化错误的东西。当您应该Sucessors()调用并行化时,您正在并行后续添加到队列中。

也就是说,Sucessors()只能从工作线程调用,而不能在“主”线程中调用。

例如,假设Sucessors()需要1秒才能运行,而您正在搜索此树:

                           o
                          / \
                         /   \
                        o     o
                       / \   / \
                      o   o o   o

您搜索此树的最快速度是3秒。您的代码需要多长时间?