并行处理队列的好策略是什么?

时间:2011-05-08 06:13:49

标签: c# queue deadlock parallel-processing

我正在编写一个需要递归搜索文件夹结构的程序,并希望与多个线程并行执行。

我已经编写了相当简单的同步方法 - 最初将根目录添加到队列,然后将目录出列,对其子目录进行排队等,直到队列为空。我将使用ConcurrentQueue<T>作为队列,但已经意识到我的循环会过早停止。第一个线程将使根目录出列,并且每个其他线程立即看到队列为空并退出,使第一个线程成为唯一运行的线程。我希望每个线程循环直到队列为空,然后等待另一个线程排队更多的目录,然后继续。我需要在循环中使用某种检查点,以便在每个线程都到达循环结束之前不会退出任何线程,但是我不确定最好的方法是在没有死锁的情况下执行此操作,而实际上没有更多的目录过程

3 个答案:

答案 0 :(得分:5)

使用Task Parallel Library

创建Task以处理第一个文件夹。在此创建一个Task来处理每个子文件夹(递归)和每个相关文件的任务。然后wait on all此文件夹的任务。

TPL运行时将利用线程池来避免创建线程,这是一项昂贵的操作。对于小件工作。

注意:

  • 如果每个文件的工作是微不足道的,那么它是内联的而不是创建另一个任务(IO性能将是限制因素)。
  • 如果避免阻塞操作,这种方法通常效果最好,但如果IO性能是极限,那么无论如何这可能无关紧要 - 开始简单和测量。
  • 在.NET 4之前,可以使用线程池完成大部分工作,但是您需要使用events等待任务完成,并且等待将占用线程池线程。 1

1 据我所知,在等待任务的TPL中 - 使用TPL方法 - TPL将重用该线程用于其他任务,直到等待完成。

答案 1 :(得分:2)

如果您想坚持使用显式队列的概念,请查看BlockingCollection类。方法GetConsumingEnumerable()返回一个IEnumerable,当集合用完项目时会阻塞,并在新项目可用时继续。这意味着只要集合为空,线程就会被阻塞,从而防止它过早停止。

但是:基本上这对生产者 - 消费者场景非常有用。我不确定你的问题是否属于这一类。

答案 2 :(得分:1)

在这种情况下,最好的选择是创建一个线程来启动,然后每当你加载子目录时,你应该从线程池中的任务线程来处理它们。允许您的线程在完成后退出,并在每次进一步进入目录时从池中调用新线程。这样就没有死锁,系统会根据需要使用线程。您甚至可以根据找到的文件夹数指定要启动的线程数。

编辑:更改上面的内容更清楚,您不希望显式创建新线程,而是希望利用线程池根据需要添加和删除线程,而无需开销。