2010-10-27 4 views
4

J'ai mis en œuvre une logique d'annulation personnalisée comme décrit dans Concurrency in Practice.Annulation personnalisée pour FutureTask

Encapsulating nonstandard cancellation in a task with newTaskFor.

Cela fonctionne très bien et je peux appeler annuler sur le futur et la tâche est annulée comme prévu. Je dois pouvoir détruire mon service d'exécuteur que j'utilise en appelant la méthode "shutdownNow", mais cette méthode appelle simplement l'interruption des threads, ce qui signifie que ma logique d'annulation personnalisée n'est jamais appelée. Comme mes tâches utilisent des sockets non bloquantes qui interrompent le thread, cela ne fonctionne pas, d'où ma logique d'annulation personnalisée.

Existe-t-il une solution simple pour annuler toutes les tâches en cours. J'ai essayé d'écraser la méthode shutdown sur ThreadPoolExecutor mais je n'ai pas accès à la liste des workers. Je voudrais pouvoir tout annuler en fermant l'exécuteur comme il est utilisé dans plusieurs endroits pour soumettre des tâches, y a-t-il une solution simple à cela?

+0

try executor.shutdownNow() – Emil

+0

oui essayé, et si vous regardez l'implémentation de shutdownNow il appelle simplement thread.interupt et ne délègue pas à la méthode d'annulation du futur. – crafty

Répondre

1

Comme John l'a souligné, il serait préférable que vous puissiez rendre vos tâches interruptibles. Ensuite, vous pouvez simplement compter sur ThreadPoolExecutor pour interrompre tous les threads de travail afin d'obtenir une annulation ordonnée.

Si cela n'est pas possible, vous pouvez ajouter une logique supplémentaire dans ThreadPoolExecutor pour obtenir ce que vous voulez. C'est un peu impliqué, et peut-être pas joli pour certains (et peut nuire à la performance), mais je pense que ça va faire le travail. Je pense fondamentalement que vous devez maintenir une liste de tâches actives vous-même. La clé est de remplacer le beforeExecute() et les méthodes afterExecute():

public class MyExecutor extends ThreadPoolExecutor { 
    private final Queue<RunnableFuture> activeTasks = 
      new LinkedBlockingQueue<RunnableFuture>(); 
    ... 

    protected void beforeExecute(Thread t, Runnable r) { 
     RunnableFuture task = (RunnableFuture)r; 
     activeTasks.add(task); 
    } 

    protected void afterExecute(Thread t, Runnable r) { 
     RunnableFuture task = (RunnableFuture)r; 
     activeTasks.remove(task); 
    } 

    public void cancelAllActiveTasks() { 
     for (RunnableFuture f: activeTasks) { 
      f.cancel(true); // invokes your custom cancellation logic 
     } 
    } 

Vous pouvez appeler cancelAllActiveTasks() ou remplacer shutdownNow() pour l'appeler ainsi. Une chose que je n'aime pas à ce sujet est d'avoir à retirer la tâche de la file d'attente car ce n'est pas une opération à temps constant.

+0

Point pris. On pourrait choisir une collection différente, comme LinkedBlockingQueue par exemple. Remove() n'est pas joli à bien des égards. :) – sjlee

+0

Oui désolé était en cliquant et accidentellement supprimé le commentaire –

+0

édité le code de ne pas utiliser le ConcurrentLinkedQueue. – sjlee