2011-09-08 9 views
6

Estoy escribiendo una aplicación que ejecuta sus acciones de menú de archivo usando SwingWorker. Cada método llamado devuelve un valor boolean que indica si la operación se ejecutó correctamente o no.¿Cómo leo los resultados de SwingWorker * sin * busy wait?

Por el momento estoy usando ocupado esperando el resultado, como esto:

public boolean executeOperation() { 
    final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { 
     @Override 
     protected Boolean doInBackground() throws Exception { 
      // .. 

      if (aborted) { 
       return false; 
      } 

      // .. 

      return true; 
     } 
    }; 

    worker.execute(); 

    // busy wait 
    while (!worker.isDone()) 
     ; 

    try { 
     return worker.get().booleanValue(); 
    } catch (Exception e) { 
     // handle exceptions .. 
     return false; 
    } 
} 

¿Existe una manera menos intensa de votación-de resolver esto?

Usando worker.get() directamente no funcionaría, ya que bloquea la EDT, esperando a que la tarea de terminar - lo que significa que incluso los diálogos de abrir desde dentro del SwingWorker no conseguiría pintado.

EDITAR: Si es posible, me gustaría evitar que el método (o el trabajador) comuniquen su resultado de forma asíncrona. Estoy implementando varios métodos cortos (archivo -> abrir, nuevo, cerrar, guardar, guardar como, salir) que dependen el uno del otro (es decir, cuando intente salir, cerrar llamadas, cerrar podría llamar guardar, guardar podría llamar guardar como). Resolver esto de forma asincrónica parece hacer que el código sea mucho más complicado.

+0

Siempre puede poner un Thread.sleep (100) en el bucle. –

+2

Esto no cambia el hecho de que está sondeando para obtener un resultado, en lugar de que el objeto que realiza el cálculo le indique cuándo está listo. – mcfinnigan

Respuesta

6

El objetivo del SwingWorker es precisamente iniciar algunas tareas en segundo plano y no bloquear el EDT. O desea algo sincrónico, y el EDT se bloqueará en cualquier cosa que intente, o si desea algo asíncrono, y la tarea en segundo plano debería actualizar su estado utilizando el método publish del SwingWorker.

Puede mostrar un diálogo modal de bloqueo con una barra de progreso mientras se está ejecutando la tarea, y ocultarlo una vez que la tarea finalice.

La alternativa es bloquear durante un tiempo, con la esperanza de que la tarea termine rápidamente, y luego realizar una copia de seguridad de manera asíncrona. Esto se puede hacer usando el get method taking a timeout como argumento.

+2

Creo que resuelve mi problema: actualmente todos mis métodos usan 'SwingWorker's, lo que parece ser un problema, ya que tengo que esperar a los resultados. Eliminaré el 'SwingWorker' de estos métodos y lo llamaré solo una vez, pero ejecutaré todo dentro del' SwingWorker' sincrónicamente. – riwi

4

Puede utilizar un paradigma asincrónico. Mire Observer/Observable y use el trabajo para transferir el resultado al objeto que está realizando el sondeo.

+0

Me gustaría evitarlo, ya que complicaría mucho mi código. Tengo varias operaciones de menú de archivo corto (archivo -> abrir, nuevo, cerrar, guardar, guardar como, salir) que dependen el uno del otro (es decir, cuando intente salir, cerrar llamadas, cerrar podría llamar guardar, guardar podría llamar guardar como). – riwi

+1

es bastante justo, agrega complejidad y si planea encadenar operaciones de esa manera, puede que no sea lo ideal. – mcfinnigan

4

Usando worker.get() directamente no funcionaría, ya que bloquea la EDT, esperando a que la tarea de terminar - lo que significa que incluso los diálogos de abrir desde dentro del SwingWorker no conseguiría pintado.

Tampoco tienen el código actual. Su espera ocupada bloquea el EDT tanto como lo hace llamar a worker.get(): solo hay un hilo de envío de eventos, y los diálogos en el SwingWorker están igual de bloqueados si ese hilo está girando en un bucle o esperando un bloqueo. El problema aquí es que si un método se ejecuta en el EDT, simplemente no puede devolver un valor de una operación asincrónica (sin acaparar el EDT) a su llamador.

La forma correcta de reaccionar al procesamiento asíncrono completado es anular el método done() en SwingWorker.

Consulte también http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html para obtener más información.

4

Una de las formas mencionadas por varias personas es anular el método hecho por SwingWorker. Sin embargo, si por algún motivo desea que el código de SwingWorker fuera del SwingWorker y el código de llamada, puede aprovechar el soporte de cambio de propiedad de SwingWorker.Simplemente agregue un PropertyChangeListener al SwingWorker y escuche la propiedad de estado que tiene un nombre de propiedad de "estado". A continuación, puede extraer el estado de SwingWorker con su método getState(). Cuando finalice, devolverá el valor DONE de SwingWorker.StateValue enum. Por ejemplo (de una respuesta que he dado en another thread aquí en SO):

if (turn == white) { 
    try { 
     final SwingWorker<Move, Void> mySwingWorker = new SwingWorker<Move, Void>() { 
      @Override 
      protected Move doInBackground() throws Exception { 
       Engine e = new Engine(); // Engine is implemented by runnable 
       e.start(); 
       Move m = e.getBestMove(board);     
       return m; 
      } 
     }; 

     mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() { 
      public void propertyChange(PropertyChangeEvent evt) { 
       if (StateValue.DONE == mySwingWorker.getState()) { 
       try { 
        Move m = mySwingWorker.get(); 

        // TODO: insert code to run on the EDT after move determined 

       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } catch (ExecutionException e) { 
        e.printStackTrace(); 
       } 
       } 
      } 
     }); 
     mySwingWorker.execute(); 

    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    } 
0

me encontré con un problema similar cuando quería una función para devolver un valor que se calcula en un trabajador de oscilación. No quería simplemente obtener ese hilo para bloquear el EDT. Yo tampoco quería que bloqueara. Así que utilicé un semáforo como este:

public boolean executeOperation() { 
    final Semaphore semaphore = new Semaphore(1); 
    semaphore.acquire(1); // surround by try catch... 
    final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { 
     @Override 
     protected Boolean doInBackground() throws Exception { 
      // .. 

      if (aborted) { 
       semaphore.release(); 
       return false; 
      } 

      // .. 
      semaphore.release(); 
      return true; 
     } 
    }; 

    worker.execute(); 



    try { 
     semaphore.tryAcquire(1, 600, TimeUnit.SECONDS); // awakes when released or when 10 minutes are up. 
     return worker.get().booleanValue(); // blocks here if the task doesn't finish in 10 minutes. 
    } catch (Exception e) { 
     // handle exceptions .. 
     return false; 
    } 
} 

Supongo que esto no es ideal para todas las situaciones. Pero pensé que era un enfoque alternativo que fue muy útil para mí.

Cuestiones relacionadas