【问题标题】:C# Multi-Threaded Tree traversalC#多线程树遍历
【发布时间】:2018-02-20 21:28:10
【问题描述】:

我正在尝试编写一个多线程遍历树结构的 C# 系统。另一种看待这一点的方式是 BlockingCollection 的消费者也是生产者。

我遇到的问题是告诉一切何时完成。 我真正需要的测试是查看是否所有线程都在 TryTake 上。 如果它们是,那么一切都已经完成,但我找不到一种方法来测试它或用任何有助于实现这一点的东西来包装它。

据我所知,下面的代码是这个代码的一个非常简单的例子,但是有一个条件,这个代码可能会失败。如果第一个线程刚刚通过 test.TryTake(out v,-1) 并且还没有执行 s.Release();它只是从集合中拉出最后一项,而第二个线程刚刚执行了 if(s.CurrentCount == 0 && test.Count ==0) 这可能返回 true,并错误地开始完成。

但随后第一个线程将继续并尝试向集合中添加更多内容。

如果我能画出线条:

if (!test.TryTake(out v, -1))
     break;
s.Release();

atomic 那么我相信这段代码会起作用。 (这显然是不可能的。)

但我不知道如何修复这个缺陷。

class Program
{

    private static BlockingCollection<int> test;

    static void Main(string[] args)
    {
        test = new BlockingCollection<int>();

        WorkClass.s = new SemaphoreSlim(2);

        WorkClass w0 = new WorkClass("A");
        WorkClass w1 = new WorkClass("B");

        Thread t0 = new Thread(w0.WorkFunction);
        Thread t1 = new Thread(w1.WorkFunction);

        test.Add(10);

        t0.Start();
        t1.Start();

        t0.Join();
        t1.Join();

        Console.WriteLine("Done");

        Console.ReadLine();
    }

    class WorkClass
    {
        public static SemaphoreSlim s;

        private readonly string _name;

        public WorkClass(string name)
        {
            _name = name;
        }

        public void WorkFunction(object t)
        {
            while (true)
            {
                int v;

                s.Wait();

                if (s.CurrentCount == 0 && test.Count == 0)
                    test.CompleteAdding();

                if (!test.TryTake(out v, -1))
                    break;
                s.Release();


                Console.WriteLine(_name + " =  " + v);
                Thread.Sleep(5);
                for (int i = 0; i < v; i++)
                    test.Add(i);
            }

            Console.WriteLine("Done " + _name);
        }
    }
}

【问题讨论】:

  • Another way to look at this is where the consumer of the BlockingCollection is also the producer. 这实际上是 ConcurrentBag 可能更高效的非常非常罕见的情况之一
  • 所以.. 你需要知道的是,当最后一个线程刚刚完成处理树的最后一个节点时,是吗?

标签: c# multithreading locking


【解决方案1】:

这可以使用任务并行来并行化。树中的每个节点都被认为是一个可能产生子任务的任务。有关更详细的说明,请参阅Dynamic Task Parallelism

对于具有 5 个级别的二叉树,将每个节点写入控制台并等待 5 毫秒,如您的示例所示,ParallelWalk 方法将如下所示:

class Program
{
    internal class TreeNode
    {

      internal TreeNode(int level)
      {
        Level = level;
      }

      internal int Level { get; }
    }

    static void Main(string[] args)
    {
      ParallelWalk(new TreeNode(0));

      Console.Read();
    }

    static void ParallelWalk(TreeNode node)
    {
      if (node == null) return;

      Console.WriteLine(node.Level);
      Thread.Sleep(5);

      if(node.Level > 4) return;

      int nextLevel = node.Level + 1;
      var t1 = Task.Factory.StartNew(
                 () => ParallelWalk(new TreeNode(nextLevel)));
      var t2 = Task.Factory.StartNew(
                 () => ParallelWalk(new TreeNode(nextLevel)));

      Task.WaitAll(t1, t2);
    }
}

中心线是任务 t1 和 t2 的生成位置。

通过这种任务分解,调度由Task Parallel Library 完成,您不必再管理一组共享的节点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-19
    • 1970-01-01
    • 2015-10-11
    • 1970-01-01
    • 2012-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多