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
  1. 彼らが報告する必要はありません。残りのジョブ数をカウントし、完了時にカウントするスレッドを減らすだけです。

  2. 残りのジョブ数がゼロになると、すべての「プロセッサ」スレッドが完了します。

  3. もちろん、そのコードをスレッドに追加するだけです。動き始めたら、時間を確認して停止時間を計算します。定期的に (たとえば、ファイルからさらに読み取りに行くとき)、停止時間を過ぎていないかどうかを確認し、過ぎている場合は停止します。

于 2012-07-24T23:07:42.467 に答える
1

wait()/notify()非常に低レベルのプリミティブであり、それらを避けたいと思うのは正しいことです。

最も簡単な解決策は、スレッドセーフなキュー (またはスタックなど - この場合はそれほど重要ではありません) を使用することです。ワーカー スレッドを開始する前に、メイン スレッドはすべてFileの をスレッド セーフなキュー/スタックに追加できます。次に、ワーカー スレッドを開始し、すべてのスレッドに s をプルさせ、スレッドFileがなくなるまで処理します。

ワーカー スレッドは、メイン スレッドが結果を取得できる別のスレッド セーフなキュー/スタックに結果を追加できます。メインスレッドは がいくつFileあったかを知っているので、同じ数の結果を取得すると、ジョブが終了したことがわかります。

のようなものjava.util.concurrent.BlockingQueueが機能し、他にもスレッドセーフなコレクションがあり、java.util.concurrentそれも問題ありません。

また、時間がかかりすぎるワーカー スレッドの終了についても尋ねました。前もって言っておきますが、ワーカー スレッドで実行されるコードを十分に堅牢にして、この機能を安全に除外できるようにできれば、作業はずっと簡単になります。

この機能が必要な場合、最も簡単で信頼性の高い解決策は、スレッドごとに「終了」フラグを設定し、ワーカー タスク コードにそのフラグを頻繁にチェックさせ、設定されている場合は終了させることです。ワーカー用のカスタム クラスを作成し、volatile booleanこの目的のためのフィールドを含めます。セッター メソッドも含めます ( であるため、volatileである必要はありませんsynchronized)。

ワーカーは、「終了」フラグが設定されていることを発見すると、そのFileオブジェクトを作業キュー/スタックにプッシュして、別のスレッドが処理できるようにすることができます。もちろん、正常に処理File できないという問題がある場合、これは無限のサイクルにつながります。

最善の方法は、ワーカー コードを非常にシンプルかつ堅牢にすることです。そのため、「終了しない」ことを心配する必要はありません。

于 2012-07-24T22:47:44.380 に答える