2012-11-22 10 views
7

Ho sentito molte volte che il modello di threading Java Swing è errato. Non capisco perfettamente perché, so che il problema è legato al fatto che è possibile disegnare su un Drawable da un altro thread diverso dal thread dell'interfaccia utente principale. So che ci sono funzionalità di utilità come SwingUtilities.invokeAndWait e SwingUtilities.invokeLater che consentono di eseguire la pittura in un Runnable, che a sua volta viene eseguito dal thread Dispatcher eventi. Immagino che in questo modo ti assicuri che la pittura sia eseguita in modo sincrono e che questo non lasci il buffer in uno stato incoerente.Perché il modello di threading Swing è considerato sbagliato e come dovrebbe essere?

La mia domanda è: come si comportano i "buoni" toolkit dell'interfaccia utente? Quali soluzioni sono adottate?

+0

Mi piacerebbe sapere perché sottovalutare questa domanda ... che cosa non va? – gotch4

+4

Avere un upvote. Penso che la tua domanda vada bene. –

+2

Forse potresti collegare un riferimento a dove l'hai sentito? – Axel

risposta

9

Brian Java Concurrency in Practice, Goetz

9,1 Perché GUI thread singolo?:

... Ai vecchi tempi, applicazioni GUI erano single-threaded e gli eventi della GUI sono stati elaborati da un “loop evento principale”. I moderni framework GUI utilizzano un modello leggermente diverso: creano un evento dedicato thread di invio (EDT) per la gestione degli eventi della GUI. I framework GUI a thread singolo non sono specifici di Java; Qt, NextStep, MacOS Cocoa, X Windows e molti altri sono anche a thread singolo. Questo non è per la mancanza di provare ; ci sono stati molti tentativi di scrivere framework GUI multithreaded , ma a causa di problemi persistenti con race condizioni e deadlock, tutti alla fine sono arrivati ​​al modello di coda eventi a thread singolo in cui un thread dedicato recupera gli eventi da una coda e dispatches li all'evento gestori definiti dalle applicazioni ...

+0

Un'altra domanda potrebbe essere, perché, invece di invocareAndWait e invokeLater, non utilizzare un approccio che racchiuda le chiamate di disegno in una coppia di metodi BEGIN_DRAW e END_DRAW, in modo che si occupino della concorrenza?L'ho visto in giro, per esempio in OGL. – gotch4

+0

@ gotch4 quei metodi effettivamente incapsulano le azioni in 'Runnable' e le pubblicano in 'EventQueue' in modo che diventino disponibili per' EventDispatchThread'. Queste azioni sono eseguite in EDT. Quando usi la demarcazione ingenua con i metodi che hai suggerito, tutto ciò che va tra di loro viene eseguito in questo thread corrente (non EDT). –

0

Swing ha una filettatura che è responsabile per consentire all'utente di interagire con la parte grafica dell'applicazione. Se esegui solo attività rapide in risposta a eventi avviati dall'utente, la tua applicazione sarà sempre reattiva.

Potrebbe verificarsi un problema se si esegue un'attività a esecuzione prolungata, da un evento avviato dall'utente, senza utilizzare un thread separato per eseguire tale attività - il problema è che, mentre l'attività è in esecuzione, l'applicazione si bloccherà. Non si verificheranno ripetizioni, l'utente non sarà in grado di interagire con tutto ciò, sembrerà che l'applicazione si sia chiusa a chiave.

Se si esegue un compito in un thread separato (per esempio, si sta scaricando una pagina e si desidera informare l'utente che il download è stato completato), allora non possono aggiornare swing direttamente da tale compito, invece devi utilizzare uno dei metodi di supporto che hai menzionato nella tua domanda.

È un processo più laborioso per creare queste attività in modo che l'applicazione sia sempre reattiva, ma se si esegue qualcosa che richiede molto tempo (il download di un file è un buon esempio), l'applicazione continuerà a essere reattivi anche mentre l'attività viene eseguita, e si può anche consentire all'utente di annullare l'attività, purché l'attività stessa lo consenta. È possibile utilizzare una finestra di dialogo modale per impedire all'utente di eseguire qualsiasi altra operazione mentre viene eseguita l'attività (se si desidera farlo) o di avere una finestra di dialogo di avanzamento che visualizza una rotellina o qualcosa del genere. Ma immagino che la cosa importante non sia lasciare che l'utente pensi che l'applicazione sia semplicemente "congelata" senza motivo.

+0

Grazie per la risposta, ma questa è la parte che conoscevo più o meno :) Mi piacerebbe sapere come viene affrontato il problema in altri sistemi UI – gotch4

+0

@ gotch4 Non lo affrontano in modo diverso. Loro praticamente funzionano così tutti loro. – nos

1

Il modo in cui vengono implementate le tecnologie di visualizzazione correnti, dipingere i pixel su uno schermo è sempre seriale. È necessario generare circa 30 immagini al secondo e dipingerle una alla volta.

Quindi non è necessario che questo disegno sia multithreading, poiché è necessario eseguire ancora la sincronizzazione in background. E questo è in realtà ciò che sta facendo Swing: utilizza un thread speciale chiamato Thread di invio eventi per pianificare tutte le modifiche che si verificano in tempo prima dell'immagine successiva.

Quindi tecnicamente, Swing è thread-safe, SE si utilizza l'EDT per inviare le modifiche. Ed ecco cosa sono i metodi invokeLater() e invokeAndWait(). Presentano modifiche all'EDT.

Se non si utilizza EDT e si invia qualche modifica a lungo termine, ad esempio il calcolo di un valore dopo la pressione di un pulsante, è possibile vedere l'applicazione non rispondere, non ridisegnarsi. Poiché l'EDT è occupato a eseguire il calcolo per te e non ha tempo per pianificare i rimborsi e altri eventi.

3

per SWT: http://book.javanb.com/swt-the-standard-widget-toolkit/ch05lev1sec7.html

SWT implementa un modello di interfaccia utente singolo thread che viene tipicamente chiamata appartamento filettatura. In questo modello, solo il thread dell'interfaccia utente può richiamare le operazioni dell'interfaccia utente. Questa regola è rigorosamente applicata. Se provi ad accedere a un oggetto SWT dall'esterno del thread dell'interfaccia utente, otterrai una SWTException ("Accesso thread non valido").

Quindi SWT è anche a thread singolo. Ma prende il passo in più per vietare qualsiasi modifica all'interfaccia utente al di fuori del thread dell'interfaccia utente. Considera l'alternativa in Swing in cui la modifica dell'interfaccia utente da qualche altra parte è consentita, ma produrrà risultati inattesi prima o poi che confonderanno il programmatore newbie che apprenderà quindi che Swing è single-threaded il modo "difficile".

Inoltre, se il tuo progetto non è chiaro, potresti finire con situazioni in cui pensi di essere nel thread corretto ma in realtà non lo sei. Puoi anche non essere in grado di dire in modo affidabile quali thread accedano a una parte specifica del codice, ma probabilmente hai comunque un problema serio di progettazione nel tuo codice.

Oltre a questo, non riesco a immaginare altri motivi per cui il modello di threading di Swing sarebbe considerato "sbagliato".

+0

In che modo SWT può rilevare che un altro thread sta interferendo con l'interfaccia utente? Tutti i metodi del toolkit sono inoltrati e viene effettuato un controllo? – gotch4

+0

Non sono sicuro poiché non ho molta esperienza con SWT, ma apparentemente no, poiché la traccia di stack dell'errore non mostra un proxy e mostra che l'eccezione deriva da metodi i cui nomi indicano che stanno controllando qualcosa, come org. eclipse.swt.widgets.Display.checkDevice. In realtà non tutti i metodi genereranno un'eccezione. Coloro che sono documentati come suscettibili di lanciare SWTException con valore ERROR_THREAD_INVALID_ACCESS. –

+0

Può controllarlo allo stesso modo di JavaFX o come lo fa Swing se si utilizza un RepaintManager personalizzato come descritto in [questo articolo] (http://weblogs.java.net/blog/alexfromsun/archive/2006/02/ debugging_swing.html) – Robin

Problemi correlati