72 votes

Comment attendre que toutes les tâches d'un ThreadPoolExecutor se terminent sans arrêter l'exécuteur?

Je ne peux pas utiliser shutdown() et awaitTermination() , car il est possible que de nouvelles tâches seront ajoutés à la ThreadPoolExecutor alors qu'il est en attente.

Donc, je suis à la recherche d'un moyen d'attendre jusqu'à ce que le ThreadPoolExecutor a vidé la file d'attente et a terminé tous ses tâches sans arrêt de nouvelles tâches d'être ajouté avant ce point.

Si cela fait une différence, c'est pour Android.

Merci

Mise à jour: Plusieurs semaines plus tard, après un réexamen de cette, j'ai découvert qu'une modification de la CountDownLatch ont mieux fonctionné pour moi dans ce cas. Je vais garder la réponse, car elle s'applique plus à ce que j'ai demandé.

79voto

Tim Bender Points 11611

Si vous souhaitez savoir quand une certaine tâche est terminée ou un certain lot de tâches, vous pouvez utiliser ExecutorService.submit(Runnable) . L'appel de cette méthode retourne un objet Future qui peut être placé dans un Collection que votre thread principal itérera ensuite lors de l'appel de Future.get() pour chacun d'eux. Cela entraînera l'arrêt de l'exécution par le thread principal jusqu'à ce que ExecutorService ait traité toutes les tâches Runnable .

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

8voto

googol4u Points 21

Mon Scénario est un robot d'indexation web pour extraire des informations d'un site web, puis de les traiter. Un ThreadPoolExecutor est utilisé pour accélérer le processus, parce que le nombre de pages peut être chargé dans le temps. Ainsi, de nouvelles tâches sera créé dans la tâche existante, car le robot suivre les liens hypertexte dans chaque page. Le problème est le même: le thread principal ne sais pas lorsque toutes les tâches sont terminées et il peut commencer à traiter le résultat. J'utilise un moyen simple de le déterminer. Il n'est pas très élégant, mais fonctionne dans mon cas:

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

6voto

Thilo Points 108673

Peut-être recherchez-vous un CompletionService pour gérer des lots de tâches, voir aussi cette réponse .

3voto

andersoj Points 10592

(Ceci est une tentative de reproduire Thilo plus tôt, supprimé réponse avec mes propres réglages.)

Je pense que vous pouvez avoir besoin de clarifier votre question car il y a un implicite infini condition... à un point que vous avez à décider d'arrêter votre exécuteur testamentaire, et à ce moment il n'accepte pas plus de tâches. Votre question semble impliquer que vous voulez attendre jusqu'à ce que vous savez qu'aucun d'autres tâches seront soumis, que vous ne connaissez dans votre propre code d'application.

La réponse suivante vous permettra de transition vers un nouveau TPE (pour quelque raison que ce soit), en remplissant tous les actuellement soumis tâches, et non pas de rejeter les nouvelles tâches de la nouvelle TPE. Il pourrait répondre à votre question. @Thilo peut aussi.

En supposant que vous avez défini dans un endroit visible TPE en usage en tant que tel:

AtomicReference<ThreadPoolExecutor> publiclyAvailableTPE = ...;

Vous pouvez alors écrire les TPE swap de routine en tant que tel. Il peut aussi être écrite à l'aide d'une méthode synchronisée, mais je pense que c'est plus simple:

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();
} 

Alternativement, si vous avez vraiment pas une blague voulez tuer le fil de la piscine, alors votre demandeur côté, la nécessité de faire face à rejeté les tâches à un certain point, et vous pouvez utiliser null pour la nouvelle TPE:

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();
} 

Ce qui pourrait causer des problèmes en amont, l'appelant aurait besoin de savoir quoi faire avec un null.

Vous pouvez également échanger avec un mannequin TPE que simplement rejetés chaque nouvelle exécution, mais c'est équivalent à ce qui se passe si vous appelez shutdown() sur le TPE.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X