【问题标题】:What's a good strategy for processing a queue in parallel?什么是并行处理队列的好策略?
【发布时间】:2011-05-08 06:13:49
【问题描述】:

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

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

【问题讨论】:

  • 您能详细说明您在文件夹结构中搜索的内容吗?只是一个文件名或文件内的东西?还是别的什么?我认为这对于帮助您使用适当的算法至关重要
  • 你这样做是为了性能吗?我查看了读取数千个文件的进程的类似情况,发现控制性能因素是磁盘 IO,并行线程中的处理不会显着影响性能。
  • 我将目录名称或其中的文件与正则表达式进行匹配,然后返回结果 - 我没有匹配文件中的任何内容。我可能会扩展它以检查文件属性,但仅此而已。
  • @ScruffyDuck - 是的,我这样做是出于性能原因,但正如我所说,我实际上并没有打开文件,只是测试它的文件名和属性。这是否应该使并行化更有效?
  • 我原以为,如果您对每个文件进行最少的处理,那么这将减少使用多个线程,而不是更有效地提高性能。在我看来,您需要对每个文件进行的处理越多,系统 IO 的影响就越小,使用多个线程来处理文件就越有用。

标签: c# queue deadlock parallel-processing


【解决方案1】:

使用Task Parallel Library

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

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

注意:

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

1 据我了解,在 TPL 中,当等待任务时——使用 TPL 方法——TPL 会将该线程重用于其他任务,直到等待完成。

【讨论】:

  • 如果我理解正确,我将完全放弃显式队列并调用操作,而不是对子目录进行排队?
  • @Daniel:是的。或者更确切地说,将显式队列替换为隐式队列(线程池中的工作项队列)。
  • 我使用这种方法遇到的问题是我不确定如何等待所有任务。 WaitAll 需要一个特定的任务列表来等待,我不会有,因为每个任务都会创建更多任务。
  • @Daniel:每个任务都会等待它直接创建的任务,其中一些任务本身会等待任务——想象一棵树,其中每个节点递归地等待其直接子节点。
【解决方案2】:

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

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

【讨论】:

  • 这是一个很好的发现,但我在适应我的多生产者多消费者场景时遇到了一点麻烦,因为只要其他生产者在生产,就没有生产者可以明确地说它已经完成.
【解决方案3】:

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

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

【讨论】:

  • 显式创建线程几乎总是错误的答案:线程池或任务会更好。
  • 我想过这个,但是创建所有线程的开销会超过收益吗?匹配一个文件或目录并不难,可能有很多需要通过。
  • @Richard - 是的,我会使用线程池并在需要时从中提取。我在想当我发布这个时,但我想我不清楚。不,您不希望从头开始创建新线程,而是希望将它们从线程池中拉出并在完成后让它们返回。
猜你喜欢
  • 2010-11-23
  • 2021-07-29
  • 1970-01-01
  • 1970-01-01
  • 2012-08-22
  • 2015-07-09
  • 2010-10-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多