C#多线程树遍历

时间:2018-02-20 21:28:10

标签: c# multithreading locking

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

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

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

但是第一个线程会继续,并尝试向集合中添加更多内容。

如果我可以划线:

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);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

这可以使用任务并行来并行化。树中的每个节点都被认为是可能产生子任务的任务。有关更详细的说明,请参阅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完成,您不再需要管理共享的节点集。