2011-12-26 1 views
2

J'ai essayé de nombreuses façons d'arrêter immédiatement une tâche qui a démarré en utilisant un ExecutorService, sans succès.Comment arrêter immédiatement une tâche démarrée à l'aide d'un ExecutorService?

Future<Void> future = executorService.submit(new Callable<Void>(
    public Void call() { 
     ... do many other things here.. 
     if(Thread.currentThread.isInterrupted()) { 
      return null; 
     } 
     ... do many other things here.. 
     if(Thread.currentThread.isInterrupted()) { 
      return null; 
     } 
    } 
)); 

if(flag) { // may be true and directly cancel the task 
    future.cancel(true); 
} 

Parfois, je dois annuler la tâche immédiatement après son démarrage, vous pouvez être curieux de savoir pourquoi je veux ce faire, eh bien, vous pouvez imaginer un sénario qu'un utilisateur touche accidentellement le bouton « Télécharger » pour démarrer une "Télécharger tâche" et il veut immédiatement annuler l'action parce que c'était juste un clic accidentel.

Le problème est que après avoir appelé future.cancel (true), la tâche n'est pas arrêté et Thread.currentThread.isInterrupted() retourne encorefaux et je n'ai aucun moyen de savoir la tâche a été arrêté depuis l'intérieur de la méthode call().

Je pense à mettre un drapeau comme annulé = true après avoir appelé la méthode future.cancel (true) et vérifier que le drapeau en permanence dans l'appel(), je pense que c'est un hack et le code pourrait être très laid car l'utilisateur peut démarrer plusieurs tâches en même temps.

Existe-t-il un moyen plus élégant de réaliser ce que je veux?

EDIT:

Ce vraiment me rend fou. J'ai passé presque une journée sur ce problème maintenant. Je vais essayer d'expliquer un peu plus le problème auquel je suis confronté.

Je fais ce qui suit pour commencer 5 tâches, chaque tâche démarrera 5 threads pour télécharger un fichier. et puis j'arrête toutes les 5 tâches immédiatement. Pour tous les appels de méthode ci-dessous, je démarre un thread (ExecutorService.submit (task)) pour le rendre asynchrone comme vous pouvez le voir à partir des suffixes des méthodes.

int t1 = startTaskAysnc(task1); 
int t2 = startTaskAysnc(task2); 
int t3 = startTaskAysnc(task3); 
int t4 = startTaskAysnc(task4); 
int t5 = startTaskAysnc(task5); 

int stopTaskAysnc(t1); 
int stopTaskAysnc(t2); 
int stopTaskAysnc(t3); 
int stopTaskAysnc(t4); 
int stopTaskAysnc(t5); 

dans startTaskAysnc(), je lance simplement une connexion socket au serveur distant pour obtenir la taille du fichier (ce qui est certainement va prendre un certain temps), après avoir obtenu avec succès le fileSize, je vais commencer 5 discussions pour télécharger différentes parties du fichier. comme ce qui suit (le code est simplifié pour le rendre plus facile à suivre):

public void startTaskAsync(DownloadTask task) { 
    Future<Void> future = executorService.submit(new Callable<Void>(
     public Void call() { 
      // this is a synchronous call 
      int fileSize = getFileSize(); 
      System.out.println(Thread.currentThread.isInterrupted()); 
      .... 
      Future<Void> futures = new Future<Void>[5]; 
      for (int i = 0; i < futures.length; ++i) { 
       futures[i] = executorService.submit(new Callable<Void>(){...}); 
      } 

      for (int i = 0; i < futures.length; ++i) { 
       futures[i].get(); // wait for it to complete 
      }    
     } 
    )); 
    synchronized (mTaskMap) { 
     mTaskMap.put(task.getId(), future); 
    } 
} 

public void stopTaskAysnc(int taskId) { 
    executorService.execute(new Runnable(){ 
     Future<Void> future = mTaskMap.get(taskId); 
     future.cancel(true); 
    }); 
} 

Je remarqué un comportement étrange qui après avoir appelé stopTaskAsync() pour les 5 tâches, il y aurait toujours au moins une tâche got s'est arrêté (c'est-à-dire Thread.currentThread.isInterrupted() renvoie true) et les 4 autres tâches sont en cours d'exécution.

Et j'ai essayé vos suggestions en définissant UncaughtExceptionHandler, mais rien ne sort de cela.

EDIT:

Le problème a été résolu dans ce lien: Can't stop a task which is started using ExecutorService

Répondre

0

Je pense que vous trouverez la solution here. Le point principal est que la méthode cancel déclenche InterruptedException. S'il vous plaît vérifiez si votre thread est toujours en cours d'exécution après l'annulation? Etes-vous sûr que vous n'avez pas essayé d'interrompre le fil fini? Etes-vous sûr que votre thread n'a pas échoué avec une autre exception?Essayez de configurer UncaughtExceptionHandler.

+0

Salut, @asdasd, j'ai édité ma question, s'il vous plaît donnez-moi de l'aide si vous avez une idée. Merci. – neevek

4

Eh bien, la javadoc de Future.cancel(boolean) dit que:

Si la tâche a déjà commencé, le paramètre mayInterruptIfRunning détermine si le thread d'exécuter cette tâche doit être interrompue pour tenter d'arrêter la tâche.

il est donc tout à fait certain que le thread qui exécute la tâche est interrompue. Ce qui aurait pu se produire est que l'un des

... faire beaucoup d'autres choses ici ..

est accidentellement le statut de compensation de interruptedThread sans effectuer la manipulation désirée. Si vous mettez un point d'arrêt dans Thread.interrupt() vous pourriez attraper le criminel.

Une autre option que je peux penser est que la tâche se termine avant de capturer l'interruption, soit parce qu'il est terminé ou levé une exception non interceptée. Appelez Future.get() pour déterminer cela. Quoi qu'il en soit, comme indiqué précédemment, il est recommandé de définir un UncaughtExceptionHandler.

+0

Salut, @yair, j'ai édité ma question, s'il vous plaît donnez-moi de l'aide si vous avez un indice. Merci. – neevek

2

Ce que vous faites est très dangereux: vous utilisez un pool de threads pour exécuter des tâches (que je vais appeler téléchargeurs), et le même pool de threads pour exécuter des tâches qui

  • attente pour la téléchargeurs à la fin (que je vais appeler les contrôleurs)
  • ou demande aux contrôleurs d'arrêter

Cela signifie que si le nombre de base de fils est atteint après que le contrôleur a commencé, les téléchargeurs seront mis en la file d'attente du pool de threads et le thread du contrôleur ne finira jamais. De même, si le nombre de threads est atteint lorsque vous exécutez la tâche d'annulation, cette tâche d'annulation sera placée dans la file d'attente et ne sera pas exécutée avant la fin d'une autre tâche.

Vous devez probablement utiliser un pool de threads pour les téléchargeurs, un autre pour les contrôleurs et le thread actuel pour annuler les contrôleurs.

+0

Salut, JB Nizet. Vous avez raison, j'en étais conscient avant de poster la question ici, mais le problème auquel je suis confronté ne semble pas avoir de rapport avec la stratégie d'utilisation du pool de threads, car je me suis assuré que le nombre minimum de threads suffirait contrôleurs et téléchargeurs pour 5 tâches. Quoi qu'il en soit, je vais prendre votre suggestion et utiliser 2 pools de threads distincts pour exécuter les contrôleurs et les téléchargeurs et annuler les contrôleurs dans le fil actuel. – neevek

Questions connexes