2010-05-08 16 views
2

Je développe une application Java Desktop. Cette application exécute la même tâche public class MyTask implements Callable<MyObject> { dans plusieurs thread simultanément.Java - SwingWorker - problème

Maintenant, quand un utilisateur clique sur un bouton "Démarrer", j'ai créé un SwingWorker myWorker et l'ai exécuté. Maintenant, myWorker crée plusieurs instances de MyTask et les soumet à un ExecutorService.

Chaque instance MyTask possède une boucle et génère un résultat intermédiaire à chaque itération. Maintenant, je veux collecter ces résultats intermédiaires de chaque instance MyTask dès qu'ils sont générés. Ensuite, après la collecte de ces résultats intermédiaires à partir de chaque instance MyTask, je veux le publier par SwingWorker.publish(MyObject) de sorte que la progression est affichée sur l'EDT.

Q1. Comment puis-je l'implémenter? Dois-je faire MyTask sous-classe de SwingWorker au lieu de Callable pour obtenir des résultats intermédiaires aussi, parce que je pense que Callable ne renvoie que le résultat final.

Q2. Si la réponse de Q1. est oui, alors pouvez-vous me donner un petit exemple pour montrer comment puis-je obtenir ces résultats intermédiaires et les agréger, puis les publier à partir SwingWorker?

Q3. Si je ne peux pas utiliser SwingWorker dans cette situation, alors comment puis-je l'implémenter?

+0

Lorsque vous collectez les résultats intermédiaires de chaque tâche, souhaitez-vous qu'ils proviennent tous de la même itération? C'est à dire. collecter tous les résultats d'itération 1, puis itération 2? – mdma

Répondre

0

Jetez un oeil à ExecutorCompletionService<T>. C'est un Executor qui fournit une méthode take pour récupérer le résultat de n'importe quelle tâche terminée.

Mise à jour:

Extension SwingWorker ne fera pas ce que vous voulez car il est spécifiquement destiné au travail déchargeant de l'EDT à un fil d'arrière-plan. Vous ne pouvez pas l'utiliser pour décharger le travail d'un thread d'arrière-plan vers d'autres threads d'arrière-plan. Les appels au SwingWorker.publish aboutissent à l'équivalent d'un SwingUtilities.invokeLater. Il n'y a aucun mécanisme que je suis conscient de faire la même chose d'un fil de fond à un fil de fond. Votre meilleur pari est de créer votre MyTask avec une référence à un Queue et d'avoir votre SwingWorker.doInBackground en file d'attente pour les résultats intermédiaires.

+0

Je veux créer plusieurs 'SwingWorker' à partir d'un' MainWorker 'SwingWorker. Ce 'mainWorker' est initié par EDT. La raison de créer plusieurs travailleurs à partir du travailleur principal est que je veux rassembler les résultats intermédiaires de tous ces travailleurs dans le mainWorker et ensuite publier ces résultats intermédiaires via. 'publish (...)' méthode de 'mainWorker'. –

-1

Un SwingWorker est également un avenir. En tant que tel, il a la méthode get() qui peut être utilisée dans la méthode done() pour obtenir le résultat de doInBackground() lorsque cette méthode se termine.

Ainsi, la construction devient un peu comme:

SwingWorker<T,P> sw=new SwingWorker<T,P>() { 

    @Override 
    public T doInBackground() throws Exception { 
    T result; 
    // do stuff here 
    return result; 
    } 

    @Override 
    public void done() { 
    try { 
     T result=get(); 
     // do stuff with result. 
    } 
    catch(ExecutionException e) { 
     Exception fromDoInBackground= (Exception) e.getCause(); 
     // handle exception thrown from doInBackground() 
    } 
    catch(InterruptedException i) { 
     // handle the case in which a SwingWorker was cancelled. typically: do nothing. 
    } 
    } 
}; 
+0

@user Je vous demande de lire attentivement la question avant de répondre ... Je sais déjà comment utiliser 'SwingWorker'. :) –

0

A1 + A2. Yatendra, est-il nécessaire que votre principal SwingWorker doive être le seul à transmettre les résultats provisoires au EDT? Si vos tâches étaient également SwingWorker instances, le travailleur principal pourrait déléguer la responsabilité de renvoyer les résultats intermédiaires au EDT et de prendre en charge le cycle de vie TaskWorkers.

package threading; 

import java.util.LinkedList; 
import java.util.List; 

import javax.swing.SwingUtilities; 
import javax.swing.SwingWorker; 

class MainSwingWorker extends SwingWorker<Void, Void> { 
    private List<TaskWorker> tasks; 

    public MainSwingWorker() { 
     tasks = new LinkedList<TaskWorker>(); 
     for(int i=0; i<2; i++) 
      tasks.add(new TaskWorker(i)); 
    } 

    @Override 
    public Void doInBackground() throws Exception { 
     Test.log("Building tasks.");      
     for(TaskWorker task : tasks) 
      launch(task); 
     Test.log("Waiting 5 secs."); 
     Thread.sleep(5000); 

     Test.log("Cancelling tasks"); 

     for(TaskWorker task : tasks) 
      task.cancel(true); 

     return null; 
    } 

    private void launch(final TaskWorker task) { 
     SwingUtilities.invokeLater(new Runnable() { 
      public void run() { 
       Test.log("Launching task worker."); 
       task.execute(); 
      } 
     });  
    } 
} 

class TaskWorker extends SwingWorker<Void, String> { 
    private int id; 

    public TaskWorker(int wid) { 
     id = wid; 
    } 

    @Override 
    public Void doInBackground() throws Exception {  
     System.out.format("[%s] Starting worker %s\n", Thread.currentThread().getName(), id); 
     while(!isCancelled()) { 
      // *************************** 
      // your task process code here 
      // *************************** 
      publish(String.format("A dummy interim result #%s", id)); 
      Thread.sleep(1000); 
     }  
     return null; 
    } 

    @Override 
    public void process(List<String> results) { 
     // it's pretty obvious, that once this method gets called you can safely 
     // call the Swing API from EDT among with the interim results 
     for(String result : results) 
      Test.log(result); 
    } 
} 

public class Test { 

    public static void log(String msg) { 
     System.out.format("[%s] %s\n", Thread.currentThread().getName(), msg); 
    } 

    public static void main(String[] args) throws Exception { 
     log("Init."); 
     SwingUtilities.invokeAndWait(new Runnable() { 
      @Override 
      public void run() { 
       log("Starting main worker."); 
       MainSwingWorker worker = new MainSwingWorker(); 
       worker.execute();       
      } 
     }); 
     Thread.sleep(7000); 
     log("Finished."); 
    } 
} 

Gardez à l'esprit que c'est juste un test, je sais qu'il ya quelques Thread.sleep(long) laid appels.

[main] Init. 
[AWT-EventQueue-0] Starting main worker. 
[SwingWorker-pool-1-thread-1] Building tasks. 
[SwingWorker-pool-1-thread-1] Waiting 5 secs. 
[AWT-EventQueue-0] Launching task worker. 
[AWT-EventQueue-0] Launching task worker. 
[SwingWorker-pool-1-thread-2] Starting worker 0 
[SwingWorker-pool-1-thread-3] Starting worker 1 
[AWT-EventQueue-0] A dummy interim result #1 
[AWT-EventQueue-0] A dummy interim result #0 
[AWT-EventQueue-0] A dummy interim result #0 
[AWT-EventQueue-0] A dummy interim result #1 
[AWT-EventQueue-0] A dummy interim result #1 
[AWT-EventQueue-0] A dummy interim result #0 
[AWT-EventQueue-0] A dummy interim result #0 
[AWT-EventQueue-0] A dummy interim result #1 
[AWT-EventQueue-0] A dummy interim result #0 
[AWT-EventQueue-0] A dummy interim result #1 
[SwingWorker-pool-1-thread-1] Cancelling tasks 
[main] Finished. 

A3 Mais si avoir une autre ExecutorService pour planifier l'exécution de votre tâche est une exigence dans votre projet, je mettre en place un mécanisme similaire publication processus pour effectuer la communication entre votre Main swing Worker fil et ce fil de la tâche . Bien qu'il semble être répétitif, vous pouvez utiliser un java.concurrent.ConcurrentQueue pour stocker les résultats provisoires dès qu'ils sont disponibles? PS: Je viens de le remarquer il y a quelques jours, mais il y a un bug ennuyeux autour de SwingWorkers qui est prevents its ExecutorService from caching unused threads.