3

我有一个应用程序,它处理存储在输入目录中的多个文件中的数据,然后根据该数据生成一些输出。

到目前为止,应用程序是按顺序工作的,即它启动一个“管理器”线程,

  • 将输入目录的内容读入File[]数组
  • 按顺序处理每个文件并存储结果
  • 处理完所有文件后终止

我想将其转换为多线程应用程序,其中“管理器”线程

  • 将输入目录的内容读入File[]数组
  • 启动多个“处理器”线程,每个线程处理单个文件、存储结果并将该文件的摘要报告返回给“管理器”线程
  • 处理完所有文件后终止

“处理器”线程的数量最多等于文件的数量,因为它们将通过ThreadPoolExecutor.

任何避免使用join()or的解决方案wait()/notify()都是可取的。

基于以上场景:

  1. 让那些“处理器”线程向“管理器”线程报告的最佳方式是什么?一个基于这里的实现Callable是否Future有意义?
  2. “管理器”线程如何知道所有“处理器”线程何时完成,即所有文件何时处理完毕?
  3. 有没有办法“定时”处理器线程并在它“太长”时终止它(即,尽管经过了预先配置的时间量,它还没有返回结果)?

任何指向(伪)源代码的指针或示例将不胜感激。

4

3 回答 3

2

你绝对可以在不使用join()or wait()/notify()你自己的情况下做到这一点。

您应该先看看java.util.concurrent.ExecutorCompletionService

在我看来,您应该编写以下类:

  • FileSummary- 保存单个文件处理结果的简单值对象
  • FileProcessor implements Callable<FileSummary>- 将文件转换为 FileSummary 结果的策略
  • File Manager- 创建 FileProcessor 实例、将它们提交到工作队列然后聚合结果的高级管理器。

FileManager 然后看起来像这样:

class FileManager {
   private CompletionService<FileSummary> cs; // Initialize this in constructor

   public FinalResult processDir(File dir) {
      int fileCount = 0;
      for(File f : dir.listFiles()) {
         cs.submit(new FileProcessor(f));
         fileCount++;
      }

      for(int i = 0; i < fileCount; i++) {
         FileSummary summary = cs.take().get();
         // aggregate summary into final result;
      }
   }

如果要实现超时,可以使用poll()CompletionService 上的方法而不是take().

于 2012-07-24T23:18:59.167 回答
1

wait()/notify()是非常低级的原语,你想要避免它们是正确的。

最简单的解决方案是使用线程安全的队列(或堆栈等 - 在这种情况下并不重要)。在启动工作线程之前,您的主线程可以将所有Files 添加到线程安全队列/堆栈中。然后启动工作线程,让它们全部拉取File并处理它们,直到没有剩余。

工作线程可以将结果添加到另一个线程安全队列/堆栈,主线程可以从中获取结果。主线程知道有多少File个 s,所以当它检索到相同数量的结果时,它就会知道作业完成了。

像 a这样的东西java.util.concurrent.BlockingQueue会起作用,并且还有其他线程安全的集合java.util.concurrent也可以。

您还询问了终止耗时太长的工作线程。我会提前告诉你:如果你能让在工作线程上运行的代码足够健壮,你可以放心地把这个特性排除在外,你会让事情变得简单得多。

如果您确实需要此功能,最简单且最可靠的解决方案是为每个线程设置一个“终止”标志,并让工作任务代码经常检查该标志并在设置时退出。为工人创建一个自定义类,并volatile boolean为此目的包含一个字段。还包括一个 setter 方法(因为volatile,它不需要是synchronized)。

如果工作人员发现其“终止”标志已设置,它可以将其File对象推回工作队列/堆栈,以便另一个线程可以处理它。当然,如果出现问题导致File 无法成功处理,这将导致无限循环。

最好的办法是使工作代码非常简单和健壮,这样您就不必担心它“不会终止”。

于 2012-07-24T22:47:44.380 回答
1
  1. 不需要他们回来报告。只需计算剩余要完成的作业数量,并在完成时减少线程计数。

  2. 当计数达到零剩余作业时,所有“处理器”线程都已完成。

  3. 当然,只需将该代码添加到线程中。当它开始工作时,检查时间并计算停止时间。定期(例如,当您从文件中读取更多内容时),检查它是否超过了停止时间,如果是,则停止。

于 2012-07-24T23:07:42.467 回答