2011-09-08 15 views
6

Sto scrivendo un'applicazione che esegue le azioni del menu file utilizzando SwingWorker. Ogni metodo chiamato restituisce un valore boolean che indica se l'operazione è stata eseguita correttamente o meno.Come si legge un risultato di SwingWorker * senza * busy busy?

Al momento sto usando occupato in attesa del risultato, in questo modo:

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; 
    } 
} 

c'è un modo meno polling-intenso di risolvere questo?

Utilizzando worker.get() direttamente non avrebbe funzionato, in quanto blocca l'EDT, in attesa che il compito di finire - il che significa anche i dialoghi apro dall'interno del SwingWorker non si sarebbe dipinta.

MODIFICA: Se possibile, vorrei evitare che il metodo (o il lavoratore) comunichi il risultato in modo asincrono. Sto implementando diversi metodi brevi (file -> apri, nuovo, chiudi, salva, salva come, uscita) che fanno affidamento l'uno sull'altro (ad esempio quando il tentativo di uscire, chiudere le chiamate chiudere, chiudere potrebbe chiamare salvare, salvare potrebbe chiamare salvare come). Risolvere questo in modo asincrono sembra rendere il codice molto più complicato.

+0

È sempre possibile inserire un Thread.sleep (100) nel loop. –

+2

Ciò non cambia il fatto che si sta eseguendo il polling per un risultato, invece di fare in modo che l'oggetto esegua il calcolo quando viene eseguito. – mcfinnigan

risposta

6

Il punto di SwingWorker è proprio quello di avviare alcune attività in background e non bloccare l'EDT. O vuoi qualcosa di sincrono, e l'EDT sarà bloccato qualunque cosa tu provi, o vuoi qualcosa di asincrono, e l'attività in background dovrebbe aggiornarne lo stato usando il metodo publish di SwingWorker.

È possibile visualizzare una finestra di dialogo modale di blocco con una barra di avanzamento mentre l'attività è in esecuzione e nasconderlo una volta completata l'attività.

L'alternativa è di bloccare per un po 'di tempo, sperando che l'attività sia veloce da finire, e quindi eseguire il backup in un modo asincrono di fare. Questo può essere fatto usando l'argomento get method taking a timeout.

+2

Penso che questo risolva il mio problema: attualmente tutti i miei metodi usano 'SwingWorker's - che sembra essere un problema, in quanto devo aspettare i risultati. Rimuoverò 'SwingWorker' da questi metodi e lo chiamerò solo una volta, ma eseguirò tutto all'interno di' SwingWorker' in modo sincrono. – riwi

4

È possibile utilizzare un paradigma asincrono. Guarda Observer/Observable e usa il lavoro per trasferire il risultato all'oggetto che sta attualmente facendo il polling.

+0

Vorrei evitarlo, poiché complicherebbe notevolmente il mio codice. Ho diverse operazioni di menu file breve (file -> aperto, nuovo, chiudi, salva, salva come, uscita) che si affidano l'un l'altro (cioè quando si tenta di uscire, chiudere le chiamate chiudere, chiudere potrebbe chiamare salvare, salvare potrebbe chiamare salvare come). – riwi

+1

equamente, aggiunge complessità e se hai intenzione di concatenare operazioni del genere potrebbe non essere l'ideale. – mcfinnigan

4

Utilizzando worker.get() direttamente non avrebbe funzionato, in quanto blocca l'EDT, in attesa che il compito di finire - il che significa anche i dialoghi apro dall'interno del SwingWorker non si sarebbe dipinta.

Neanche con il codice corrente. L'attesa intensa blocca l'EDT quanto lo chiama lo worker.get() - c'è solo un thread di invio di un evento e le finestre di dialogo nello SwingWorker sono bloccate se il thread sta girando in loop o in attesa di un blocco. Il problema qui è che se un metodo viene eseguito su EDT, semplicemente non può restituire un valore da un'operazione asincrona (senza eseguire il log degli EDT) al relativo chiamante.

Il modo corretto di reagire all'elaborazione asincrona completata sostituisce il metodo done() in SwingWorker.

Controllare anche http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html per ulteriori informazioni.

4

Un modo come indicato da più persone sopra è quello di ignorare il metodo eseguito da SwingWorker. Tuttavia, se per qualche motivo si desidera che il codice di SwingWorker venga inserito al di fuori di SwingWorker e nel codice di chiamata, è possibile usufruire del supporto per la modifica delle proprietà di SwingWorker.Basta aggiungere un PropertyChangeListener a SwingWorker e ascoltare la proprietà di stato che ha il nome di una proprietà di "stato". È quindi possibile estrarre lo stato di SwingWorker con il suo metodo getState(). Al termine restituirà il valore DONE dell'enumerazione SwingWorker.StateValue. Per esempio (da una risposta che ho dato a another thread qui su 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

mi sono imbattuto in un problema simile quando ho voluto una funzione per restituire un valore che sarebbe stato calcolato in un operaio swing. Non volevo semplicemente ottenere quel thread per bloccare l'EDT. Anche io non volevo che bloccasse. Quindi ho usato un semaforo come questo:

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; 
    } 
} 

Immagino che questo non sia l'ideale per tutte le situazioni. Ma ho pensato che fosse un approccio alternativo che è stato molto utile per me.

Problemi correlati