2011-05-24 66 views
6

Actualmente tengo dos hilos SwingWorker trabajando en segundo plano. Si se produce una excepción, el método se detiene para funcionar, pero el hilo aún se ejecuta.¿Cómo se cancela la ejecución de un SwingWorker?

¿Cómo hago para detener la ejecución y matar el hilo del doInBackground() si ocurre una excepción?

this.cancel(true) No destruir/cerrar el hilo. ¿Cómo puedo conseguir esto?

@Override 
protected Boolean doInBackground() throws Exception { 
     try { 
      while (true) { 
       //some code here     
       return true; 
      } 
     } catch (Exception e) {  
      this.cancel(true); //<-- this not cancel the thread    
      return false; 
     } 
    } 

Veo estos hilos en la depuración de Netbeans.

'AWT-EventQueue-0' em execução 
'AWT-Windows' em execução 
'SwingWorker-pool-1-thread-1' em execução 
'SwingWorker-pool-1-thread-2' em execução 

//*em execução = in execution 
+0

¿Puede confirmar que 'doInBackground()' devuelve y no bloquea dentro del bucle 'while (true)'? – jfpoilpret

+0

Sí, depuré el código y entra en la excepción que devuelve falso. –

Respuesta

7

Por defecto, SwingWorker reutiliza subprocesos de trabajo, lo que es perfectamente normal que, a pesar de que, doInBackground() ha vuelto, todavía a ver el hilo que ejecuta su método.

se puede identificar este hecho al mirar los nombres de hilo, según lo informado por NetBeans: SwingWorker-pool-1-thread-1, en las que piscina está gestionado por SwingWorker.

Si desea obtener más control, también puede pasar la instancia SwingWorker a Executor.

Simplemente marque SwingWorker y Executor javadoc para obtener más información.

Además, SwingWorker.cancel() se supone que no debe llamarse desde doInBackground() sino desde otro hilo, generalmente el EDT, p. Ej. cuando el usuario hace clic en Cancelar el botón en un cuadro de diálogo de progreso.

+0

No se reutiliza, cada vez que hago clic en el botón para el proceso que se ejecutará en segundo plano, crea un nuevo hilo. Intentaré llamar desde otro hilo para ver qué pasa. –

+0

Difícil de decir, pero no se puede dominar la forma en que 'SwingWorker' trata con los grupos de subprocesos y la asignación de una instancia de' SwingWorker' determinada a un subproceso del grupo. Como dije, si desea un control total, puede usar una de las implementaciones existentes de 'Executor', o crear la suya propia, y pasar sus instancias' SwingWorker' a su método 'execute'. – jfpoilpret

+0

de acuerdo, pero Executor más SwingWorker 1) todavía en top25 http://bugs.sun.com/top25_bugs.do 2) si realmente quieres Multithreading, entonces tienes que nombrar tus hilos 3) agregar PropertyChangeListener para SwingWorker, entonces puedes conocer los estados de SwingWorker 4) ser caffefull con el número de Threads simultáneos iniciados desde Executor, porque Executor no sabe si SwingWorker finaliza o no 5) para evitar cualquiera de las desventajas de poner aquí el valor booleano booleano, luego solo verificar el valor de PropertyChangeListener con el último line from done(), que devuelve true a confirmado que el hilo termina sin ningún error – mKorbel

4

hay un método cancel(). Tu código debería prestar atención a eso. Si continúa ejecutándose después de una excepción, parecería que su código está ignorando una excepción que no debería ser.

+0

Sí, veo algo como esto, pero este método se llama dentro de la excepción con un this.cancel (verdadero); o en clase que llama al método de ejecución de SwingWorker? –

+1

El método cancelar está destinado a los objetos que ejecutan ejecutar. Si el SwingWorker necesita dejar de ejecutarse debido a una Excepción, entonces esa Excepción debe manejarse correctamente. – jzd

9

como jzd mencionado, existe el método cancel(boolean mayInterruptIfRunning); para exapmle

EDITAR: con cancelar (verdadero); usted tiene que (siempre) cautgh una excepción java.util.concurrent.CancellationException

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.*; 
import java.util.ArrayList; 

public class SwingWorkerExample extends JFrame implements ActionListener { 

    private static final long serialVersionUID = 1L; 
    private final JButton startButton, stopButton; 
    private JScrollPane scrollPane = new JScrollPane(); 
    private JList listBox = null; 
    private DefaultListModel listModel = new DefaultListModel(); 
    private final JProgressBar progressBar; 
    private mySwingWorker swingWorker; 

    public SwingWorkerExample() { 
     super("SwingWorkerExample"); 
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     getContentPane().setLayout(new GridLayout(2, 2)); 
     startButton = makeButton("Start"); 
     stopButton = makeButton("Stop"); 
     stopButton.setEnabled(false); 
     progressBar = makeProgressBar(0, 99); 
     listBox = new JList(listModel); 
     scrollPane.setViewportView(listBox); 
     getContentPane().add(scrollPane); 
     //Display the window. 
     pack(); 
     setVisible(true); 
    } 
//Class SwingWorker<T,V> T - the result type returned by this SwingWorker's doInBackground 
//and get methods V - the type used for carrying out intermediate results by this SwingWorker's 
//publish and process methods 

    private class mySwingWorker extends javax.swing.SwingWorker<ArrayList<Integer>, Integer> { 
//The first template argument, in this case, ArrayList<Integer>, is what s returned by doInBackground(), 
//and by get(). The second template argument, in this case, Integer, is what is published with the 
//publish method. It is also the data type which is stored by the java.util.List that is the parameter 
//for the process method, which recieves the information published by the publish method. 

     @Override 
     protected ArrayList<Integer> doInBackground() { 
//Returns items of the type given as the first template argument to the SwingWorker class. 
      if (javax.swing.SwingUtilities.isEventDispatchThread()) { 
       System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() returned true."); 
      } 
      Integer tmpValue = new Integer(1); 
      ArrayList<Integer> list = new ArrayList<Integer>(); 
      for (int i = 0; i < 100; i++) { 
       for (int j = 0; j < 100; j++) { //find every 100th prime, just to make it slower 
        tmpValue = FindNextPrime(tmpValue.intValue()); 
//isCancelled() returns true if the cancel() method is invoked on this class. That is the proper way 
//to stop this thread. See the actionPerformed method. 
        if (isCancelled()) { 
         System.out.println("SwingWorker - isCancelled"); 
         return list; 
        } 
       } 
//Successive calls to publish are coalesced into a java.util.List, which is what is received by process, 
//which in this case, isused to update the JProgressBar. Thus, the values passed to publish range from 
//1 to 100. 
       publish(new Integer(i)); 
       list.add(tmpValue); 
      } 
      return list; 
     }//Note, always use java.util.List here, or it will use the wrong list. 

     @Override 
     protected void process(java.util.List<Integer> progressList) { 
//This method is processing a java.util.List of items given as successive arguments to the publish method. 
//Note that these calls are coalesced into a java.util.List. This list holds items of the type given as the 
//second template parameter type to SwingWorker. Note that the get method below has nothing to do with the 
//SwingWorker get method; it is the List's get method. This would be a good place to update a progress bar. 
      if (!javax.swing.SwingUtilities.isEventDispatchThread()) { 
       System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() + returned false."); 
      } 
      Integer percentComplete = progressList.get(progressList.size() - 1); 
      progressBar.setValue(percentComplete.intValue()); 
     } 

     @Override 
     protected void done() { 
      System.out.println("doInBackground is complete"); 
      if (!javax.swing.SwingUtilities.isEventDispatchThread()) { 
       System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() + returned false."); 
      } 
      try { 
//Here, the SwingWorker's get method returns an item of the same type as specified as the first type parameter 
//given to the SwingWorker class. 
       ArrayList<Integer> results = get(); 
       for (Integer i : results) { 
        listModel.addElement(i.toString()); 
       } 
      } catch (Exception e) { 
       System.out.println("Caught an exception: " + e); 
      } 
      startButton(); 
     } 

     boolean IsPrime(int num) { //Checks whether a number is prime 
      int i; 
      for (i = 2; i <= num/2; i++) { 
       if (num % i == 0) { 
        return false; 
       } 
      } 
      return true; 
     } 

     protected Integer FindNextPrime(int num) { //Returns next prime number from passed arg.  
      do { 
       if (num % 2 == 0) { 
        num++; 
       } else { 
        num += 2; 
       } 
      } while (!IsPrime(num)); 
      return new Integer(num); 
     } 
    } 

    private JButton makeButton(String caption) { 
     JButton b = new JButton(caption); 
     b.setActionCommand(caption); 
     b.addActionListener(this); 
     getContentPane().add(b); 
     return b; 
    } 

    private JProgressBar makeProgressBar(int min, int max) { 
     JProgressBar progressBar1 = new JProgressBar(); 
     progressBar1.setMinimum(min); 
     progressBar1.setMaximum(max); 
     progressBar1.setStringPainted(true); 
     progressBar1.setBorderPainted(true); 
     getContentPane().add(progressBar1); 
     return progressBar1; 
    } 

    private void startButton() { 
     startButton.setEnabled(true); 
     stopButton.setEnabled(false); 
     System.out.println("SwingWorker - Done"); 
    } 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     if ("Start" == null ? e.getActionCommand() == null : "Start".equals(e.getActionCommand())) { 
      startButton.setEnabled(false); 
      stopButton.setEnabled(true); 
// Note that it creates a new instance of the SwingWorker-derived class. Never reuse an old one. 
      (swingWorker = new mySwingWorker()).execute(); // new instance 
     } else if ("Stop" == null ? e.getActionCommand() == null : "Stop".equals(e.getActionCommand())) { 
      startButton.setEnabled(true); 
      stopButton.setEnabled(false); 
      swingWorker.cancel(true); // causes isCancelled to return true in doInBackground 
      swingWorker = null; 
     } 
    } 

    public static void main(String[] args) { 
// Notice that it kicks it off on the event-dispatching thread, not the main thread. 
     SwingUtilities.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       SwingWorkerExample swingWorkerExample = new SwingWorkerExample(); 
      } 
     }); 
    } 
} 
+0

El método isCancelled() devuelve true después del método cancel(), pero ¿por qué el hilo 'SwingWorker-pool-1-thread-1' aún aparece como en ejecución? –

+0

Aparece también el 'SwingWorker-pool-1-thread-2'. –

+0

buena explicación como todas aquí (+1 todas). El ejemplo es colosal, pero me gusta gracias :) – Boro

0

Hasta El SwingWoker lo arreglen http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6826514 Aquí una versión simple (probado) con las funciones básicas (similares) que luego SwingWoker

/* 
* To change this template, choose Tools | Templates 
* and open the template in the editor. 
*/ 
package tools; 

import java.util.LinkedList; 
import java.util.List; 
import javax.swing.SwingUtilities; 

/** 
* 
* @author patrick 
*/ 
public abstract class MySwingWorker<R,P> { 

    protected abstract R doInBackground() throws Exception; 
    protected abstract void done(R rvalue, Exception ex, boolean canceled); 
    protected void process(List<P> chunks){} 
    protected void progress(int progress){} 

    private boolean cancelled=false; 
    private boolean done=false; 
    private boolean started=false; 
    final private Object syncprogress=new Object(); 
    boolean progressstate=false; 
    private int progress=0; 
    final private Object syncprocess=new Object(); 
    boolean processstate=false; 
    private LinkedList<P> chunkes= new LinkedList<>(); 

    private Thread t= new Thread(new Runnable() { 
     @Override 
     public void run() { 
      Exception exception=null; 
      R rvalue=null; 
      try { 
       rvalue=doInBackground(); 
      } catch (Exception ex) { 
       exception=ex; 
      } 

      //Done: 
      synchronized(MySwingWorker.this) 
      { 
       done=true; 
       final Exception cexception=exception; 
       final R crvalue=rvalue; 
       final boolean ccancelled=cancelled; 

       SwingUtilities.invokeLater(new Runnable() { 
        @Override 
        public void run() { 
         done(crvalue, cexception, ccancelled); 
        } 
       }); 
      } 

     } 
    });  

    protected final void publish(P p) 
    { 
     if(!Thread.currentThread().equals(t)) 
      throw new UnsupportedOperationException("Must be called from worker Thread!"); 
     synchronized(syncprocess) 
     { 
      chunkes.add(p); 
      if(!processstate) 
      { 
       processstate=true; 
       SwingUtilities.invokeLater(new Runnable() { 
        @Override 
        public void run() { 
         List<P> list; 
         synchronized(syncprocess) 
         { 
          MySwingWorker.this.processstate=false; 
          list=MySwingWorker.this.chunkes; 
          MySwingWorker.this.chunkes= new LinkedList<>(); 
         } 
         process(list); 
        } 
       }); 
      } 
     } 
    } 

    protected final void setProgress(int progress) 
    { 
     if(!Thread.currentThread().equals(t)) 
      throw new UnsupportedOperationException("Must be called from worker Thread!"); 
     synchronized(syncprogress) 
     { 
      this.progress=progress; 
      if(!progressstate) 
      { 
       progressstate=true; 
       SwingUtilities.invokeLater(new Runnable() { 
        @Override 
        public void run() { 
         int value; 
         //Acess Value 
         synchronized(syncprogress) 
         { 
          MySwingWorker.this.progressstate=false; 
          value=MySwingWorker.this.progress; 
         } 
         progress(value); 
        } 
       }); 
      } 
     } 
    } 

    public final synchronized void execute() 
    { 
     if(!started) 
     { 
      started=true; 
      t.start(); 
     } 
    } 

    public final synchronized boolean isRunning() 
    { 
     return started && !done; 
    } 

    public final synchronized boolean isDone() 
    { 
     return done; 
    } 

    public final synchronized boolean isCancelled() 
    { 
     return cancelled; 
    } 

    public final synchronized void cancel() 
    { 
     if(started && !cancelled && !done) 
     { 
      cancelled=true; 
      if(!Thread.currentThread().equals(t)) 
       t.interrupt(); 
     } 
    } 

} 
Cuestiones relacionadas