web-dev-qa-db-ja.com

executorをシャットダウンせずにThreadPoolExecutorのすべてのタスクが完了するのを待つ方法は?

待機中にThreadPoolExecutorに新しいタスクが追加される可能性があるため、shutdown()およびawaitTermination()を使用できません。

そのため、ThreadPoolExecutorがキューを空にし、その前に新しいタスクの追加を停止せずにすべてのタスクを完了するまで待機する方法を探しています。

違いがある場合、これはAndroid向けです。

ありがとう

更新:これを何度か見てから数週間後、この場合、修正されたCountDownLatchの方がうまく機能していることがわかりました。答えはマークしたままにします。これは、私が尋ねたものにさらに当てはまるためです。

58
cottonBallPaws

特定のタスクが完了したとき、またはタスクの特定のバッチをいつ知りたいかを知りたい場合は、ExecutorService.submit(Runnable)を使用できます。このメソッドを呼び出すと、Futureオブジェクトが返されます。このオブジェクトは、Collectionに配置でき、メインスレッドは、それぞれのFuture.get()の呼び出しを繰り返します。これにより、メインスレッドは、ExecutorServiceRunnableタスクをすべて処理するまで実行を停止します。

Collection<Future<?>> futures = new LinkedList<Future<?>>();
futures.add(executorService.submit(myRunnable));
for (Future<?> future:futures) {
    future.get();
}
68
Tim Bender

私のシナリオは、Webサイトから情報を取得して処理するWebクローラーです。多くのページを一度にロードできるため、ThreadPoolExecutorを使用してプロセスを高速化します。クローラーは各ページのハイパーリンクをたどるため、既存のタスクに新しいタスクが作成されます。問題は同じです。メインスレッドは、すべてのタスクがいつ完了するかわからず、結果の処理を開始できます。簡単な方法でこれを判断します。それは非常にエレガントではありませんが、私の場合は動作します:

while (executor.getTaskCount()!=executor.getCompletedTaskCount()){
    System.err.println("count="+executor.getTaskCount()+","+executor.getCompletedTaskCount());
    Thread.sleep(5000);
}
executor.shutdown();
executor.awaitTermination(60, TimeUnit.SECONDS);
7
googol4u

タスクのバッチを管理するために CompletionService を探しているのかもしれません。 this answer も参照してください。

5
Thilo

(これは、私自身の調整で、Thiloの以前の削除された回答を再現する試みです。)

暗黙の無限条件があるため、質問を明確にする必要があるかもしれません...ある時点でエグゼキューターをシャットダウンすることを決めなければならず、その時点でそれ以上のタスクを受け入れません。あなたの質問は、あなたがknowそれ以上のタスクが送信されないことを待つことを暗示しているようです。これはあなた自身のアプリケーションコードでのみ知ることができます。

次の回答により、(何らかの理由で)新しいTPEにスムーズに移行し、現在送信されているすべてのタスクを完了し、新しいTPEへの新しいタスクを拒否することはできません。あなたの質問に答えるかもしれません。 @Thiloの可能性もあります。

使用中の目に見えるTPEをどこかで定義したと仮定します:

_AtomicReference<ThreadPoolExecutor> publiclyAvailableTPE = ...;
_

その後、TPEスワップルーチンをそのまま記述できます。同期メソッドを使用して作成することもできますが、これは簡単だと思います。

_void rotateTPE()
{
   ThreadPoolExecutor newTPE = createNewTPE();
   // atomic swap with publicly-visible TPE
   ThreadPoolExecutor oldTPE = publiclyAvailableTPE.getAndSet(newTPE);
   oldTPE.shutdown();

   // and if you want this method to block awaiting completion of old tasks in  
   // the previously visible TPE
   oldTPE.awaitTermination();
} 
_

また、スレッドプールを強制的に削除したくない場合は、送信者側が拒否されたタスクにいつか対処する必要があり、新しいTPEにnullを使用できます。

_void killTPE()
{
   ThreadPoolExecutor oldTPE = publiclyAvailableTPE.getAndSet(null);
   oldTPE.shutdown();

   // and if you want this method to block awaiting completion of old tasks in  
   // the previously visible TPE
   oldTPE.awaitTermination();
} 
_

アップストリームの問題を引き起こす可能性があるため、呼び出し元はnullをどうするかを知る必要があります。

また、単にすべての新しい実行を拒否するダミーのTPEと交換することもできますが、これはTPEでshutdown()を呼び出した場合と同じです。

3
andersoj

shutdownを使用したくない場合は、以下のアプローチに従ってください。

  1. FutureでのサブミットからすべてのExecutorServiceタスクを反復処理し、_Tim Bender_が示唆するFutureオブジェクトのget()オブジェクトのブロック呼び出しでステータスを確認します。

  2. のいずれかを使用

    1. ExecutorServiceinvokeAll を使用する
    2. CountDownLatch の使用
    3. ForkJoinPool または newWorkStealingPool of Executors(since Java 8)の使用

executorサービスのinvokeAll()CountDownLatchと同じ目的を達成します

関連するSEの質問:

スレッドの数が完了するのを待つ方法

1
Ravindra babu

waitTillDone()Runnerクラスで呼び出すことができます:

Runner runner = Runner.runner(10);

runner.runIn(2, SECONDS, runnable);
runner.run(runnable); // each of this runnables could submit more tasks

runner.waitTillDone(); // blocks until all tasks are finished (or failed)

// and now reuse it

runner.runIn(500, MILLISECONDS, callable);

runner.waitTillDone();
runner.shutdown();

使用するには、このgradle/maven依存関係をプロジェクトに追加します:'com.github.matejtymes:javafixes:1.0'

詳細については、こちらをご覧ください: https://github.com/MatejTymes/JavaFixes またはこちら: http://matejtymes.blogspot.com/2016/04/executor-that-notifies -you-when-task.html

0
Matej Tymes

以下に示すように、キューサイズとアクティブなタスクカウントを使用してみてください。

 while (executor.getThreadPoolExecutor().getActiveCount() != 0 || !executor.getThreadPoolExecutor().getQueue().isEmpty()){
                     try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
0