|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#1 |
|
Member
Iscritto dal: Jan 2007
Messaggi: 173
|
[Java] Comunicazione tra thread
Salve a tutti
Ho scritto un programmino che parte creando una piccola GUI Dietro un pulsante della GUI vi è un listener. Se premuto, si attiva il listener che crea un thread; in questo thread è eseguito il mio programma, è istanziata una certa classe e lanciato un suo metodo mooooooolto lungo. Vorrei fare in modo che i 2 thread comunicassero e cioè che il secondo, durante l'esecuzione del metodo moooooolto lungo, mandi delle stringhe al primo, in modo che il primo possa visualizzarle in una JTextArea Ad esempio invii: operazione 1 completata, inizio operazione 2, ..... completato.... Giusto per far capire all'utente che non è tutto bloccato ma ci vuole tempo e deve aspettare. Come faccio a inviare queste stringhe dal secondo thread che esegue un codice in un file java separato dal primo, e a farle ricevere dal primo? Grazie 1000 |
|
|
|
|
|
#2 |
|
Senior Member
Iscritto dal: Nov 2004
Messaggi: 691
|
Potresti fare l'oggetto thread lavoratore un Observable, quello che lo starta un Observer, e registrare l'Observable, che manderà notifiche ai suoi Observer man mano che la computazione va avanti!
|
|
|
|
|
|
#3 |
|
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Il suggerimento di tylerdurden83 è valido.
Esiste poi una classe specifica per il tipo di situazione in cui ti trovi (un thread in background che vuole "parlare" alla gui), SwingWorker. Anzichè usare un thread nudo e crudo usi uno SwingWorker che funziona come un thread in background e in più ha un paio di metodi pensati proprio per inviare notifiche in corso d'opera ad elementi dell'interfaccia grafica. Supponiamo che adesso il tuo codice somigli a questo: Codice:
...actionPerformed(ActionEvent e) {
new Thread() {
public void run() {
...vai con l'operazione di lungo termine
}
}.start();
}
Codice:
...actionPerformed(ActionEvent e) {
new SwingWorker<Void, String>() {
protected void doInBackground() throws Exception {
...vai con l'operazione di lungo termine
}
protected void process(List<String> chunks) {
}
}.execute();
}
Codice:
...actionPerformed(ActionEvent e) {
new SwingWorker<Void, String>() {
protected void doInBackground() throws Exception {
publish("Partenza");
...vai con l'operazione in background
publish("Finito il passaggio 1");
...continua con l'operazione in background
publish("Finito il passaggio 2");
...continua
publish("Procedimento terminato");
}
protected void process(List<String> chunks) {
for(String message : chunks) {
jTextArea.append(message + "\n");
}
}
}.execute();
}
Pubblichi una stringa ma processi una lista perchè, per ragioni d'efficienza, lo SwingWorker compatta pubblicazioni multiple in una sola notifica (magari il processo in background impiega un nanosecondo per tre passaggi di stato, lo SwingWorker anzichè fare tre salti nell'EDT ne fa uno solo con una lista di messaggi).
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
|
|
|
|
#4 |
|
Member
Iscritto dal: Jan 2007
Messaggi: 173
|
Innanzitutto grazie per le risposte.
Al momento il codice dietro il mio bottone sarebbe questo Codice:
final popolamentoOntologia po = new popolamentoOntologia();
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
status.setText("Stato: Esecuzione...");
b.setEnabled(false);
p4.setEnabled(false);
Thread t = new Thread() {
public void run() {
try {
po.ontologyPopolation(getPathFashion(), getPathMondrian(), getPathOntology());
} catch (Exception e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
status.setText("Stato: Caricamento completato");
// b.setEnabled(true);
}
});
}
};
t.start();
}
});
Non posso dividerlo. Posso usare i publish nel metodo di popolamentoOntologia? Ultima modifica di k_mishima : 16-05-2011 alle 21:01. |
|
|
|
|
|
#5 |
|
Senior Member
Iscritto dal: Nov 2004
Messaggi: 691
|
sorry ero al tel. allora vediamo un po, nei commenti ho aggiunto cosa non va bene
Codice:
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
// il metodo actionPerformed è eseguito nell'EDT
status.setText("Stato: Esecuzione...");
b.setEnabled(false);
p4.setEnabled(false);
// creare questo thread che fa il lavoro (ontologyPopolation) e startarlo qui dentro, ossia nell'EDT, NON è corretto. Devi usare un background thread come ti ha detto PGI-Bis
Thread t = new Thread() {
public void run() {
try {
po.ontologyPopolation(getPathFashion(), getPathMondrian(), getPathOntology());
} catch (Exception e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
status.setText("Stato: Caricamento completato");
// b.setEnabled(true);
}
});
}
};
t.start();
}
});
1) Partiamo dalla classe con il metodo che fa l'elaborazione tosta. Quella classe la dichiari in modo che extends Observable 2) Aggiungi un metodo tipo questo (nel mio caso è protected perchè sta in una superclasse): Codice:
/**
* Set this Observable's state as "changed" and notify its Observers.
*/
protected void changedState(int counter) {
setChanged();
notifyObservers(counter);
}
3) Aggiungo al metodo che "elabora" questa istruzione: Codice:
// notify the Observers that another configuration has been tested super.changedState(counter++); 4) La mia classe che gestisce il lancio della computazione è dichiarata così: Codice:
public class FireComputationTask extends Task<Player, Void> implements Observer, PropertyChangeListener {
5) Copia lo stato della gui in variabili d'istanza di questa classe, ad es: Codice:
private final String indirizzo; public FireComputationTask(String indirizzo) this.indirizzo=indirizzo (indirizzo ad es potrebbe essere passato al costruttore facendo jtextbox.getText(); Codice:
/**
* Executed in a background thread to find out the best Player obtainable from this AbstractGearOptimization instance.
* @return the best Player obtainable from this AbstractGearOptimization instance
*/
@Override protected Player doInBackground() {
// register this to call propertyChange when progress changes via setProgress
this.addPropertyChangeListener(this);
// add this to the list of observers of the AbstractGearOptimization
this.goa.addObserver(this);
// 100 / the number of combinations to test represents the percentage value of every single test
this.percentage = (float)100/this.combinations;
//Initialize progress property.
super.setProgress(0);
System.out.println("["+DateFormat.format(new Date())+"] Starting the optimization using "+this.goa.getClass());
// call the compute method to fire the actual optimization computation and retrieve the best Player for the passed GearOptimizationAlgorithm
return this.goa.compute();
}
7) In quanto Observer, implemento update Codice:
public void update(Observable o, Object arg) {
super.setProgress(Math.min((Integer)arg*this.percentage/100, 100));
}
Codice:
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equalsIgnoreCase(evt.getPropertyName())) {
int progress = (Integer) evt.getNewValue();
// set the progress bar to the new progress value
this.jprogressBar.setValue(progress);
}
}
Codice:
@Override protected void succeeded(Player result) {
// dispose the progress bar frame
this.progressBarFrame.dispose();
...
}
- doInBackground è eseguito in un background thread. La chiamata al tuo metodo calcolatore DEVE essere fatta qui dentro, e non puoi accedere ai componenti gui - succeed è eseguito nell'EDT una volta che doInBackground è terminato, quindi qui puoi accedere ai componenti gui, ma non eseguire task Naturalmente anche la soluzione di PGI è corretta, la differenza sta principalmente nel fatto che con l'adozione di Observer-Observable hai notifiche sul progress anche fuori da Swing, quindi che ne so domani vuoi fare un utility per usarlo via command line il tuo programma, e già hai tutto pronto. Ultima modifica di tylerdurden83 : 16-05-2011 alle 22:31. |
|
|
|
|
|
#6 |
|
Member
Iscritto dal: Jan 2007
Messaggi: 173
|
sembra complicato
provo e ti faccio sapere, ma ci vorrà un bel po mi sa, domani posto come è andata |
|
|
|
|
|
#7 |
|
Senior Member
Iscritto dal: Nov 2004
Messaggi: 691
|
Na, guarda queste due classi di esempio che ho appena scritto, prova anche a lanciarle:
Codice:
import java.util.Observable;
public class Worker extends Observable {
public boolean doWork(){
for(int i=0; i<10; i++){
System.out.print("Working....");
try {
Thread.sleep((int)(5000 * Math.random()));
} catch (InterruptedException ex) {
return false;
}
this.changedState(i);
}
return true;
}
private void changedState(int counter) {
setChanged();
notifyObservers(counter);
}
}
Codice:
import java.util.Observable;
import java.util.Observer;
public class Launcher implements Observer {
public void update(Observable o, Object arg) {
System.out.println(" blocchetto "+arg+" terminato.");
}
public static void main(String[] args){
Launcher launcher = new Launcher();
Worker worker = new Worker();
worker.addObserver(launcher);
worker.doWork();
}
}
|
|
|
|
|
|
#8 |
|
Member
Iscritto dal: Jan 2007
Messaggi: 173
|
si, la situazione è questa.
|
|
|
|
|
|
#9 |
|
Senior Member
Iscritto dal: Nov 2004
Messaggi: 691
|
Codice:
private class PopolaOntologiaTaskTask extends org.jdesktop.application.Task<Boolean, Void> implements Observer {
private final int contatore;
private final Worker worker;
private final JProgressBar progressBar;
private final JFrame frame = new JFrame();
PopolaOntologiaTaskTask(Application app, int contatore) {
// Runs on the EDT. Copy GUI state that
// doInBackground() depends on from parameters
// to PopolaOntologiaTaskTask fields, here.
super(app);
this.contatore=contatore;
this.worker=new Worker();
this.progressBar=new JProgressBar();
this.progressBar.setValue(0);
this.progressBar.setStringPainted(true);
this.progressBar.setMinimum(0);
this.progressBar.setMaximum(this.contatore);
this.frame.add(this.progressBar);
this.frame.setLocationRelativeTo(null);
this.frame.pack();
this.frame.setVisible(true);
}
@Override protected Boolean doInBackground() {
// Your Task's code here. This method runs
// on a background thread, so don't reference
// the Swing GUI from here.
this.worker.addObserver(this);
return this.worker.doWork(this.contatore);// return your result
}
@Override protected void succeeded(Boolean result) {
try {
this.frame.dispose();
// Runs on the EDT. Update the GUI based on
// the result computed by doInBackground().
if (this.get() == true) {
JOptionPane.showMessageDialog(null, "TUTTO OK", "CONGRATULAZIONI", JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(null, "KO", "DANNAZIONE", JOptionPane.ERROR_MESSAGE);
}
} catch (InterruptedException ex) {
} catch (ExecutionException ex) {
}
}
public void update(Observable o, Object arg) {
System.out.println("Setting the progress bar to "+Integer.parseInt(arg.toString()));
this.progressBar.setValue(Integer.parseInt(arg.toString()));
}
}
Ultima modifica di tylerdurden83 : 16-05-2011 alle 23:07. |
|
|
|
|
|
#10 |
|
Senior Member
Iscritto dal: Nov 2004
Messaggi: 691
|
|
|
|
|
|
|
#11 |
|
Member
Iscritto dal: Jan 2007
Messaggi: 173
|
Non riesco a farlo, cambiando la definizione della classe mi si creano errori in seguito, purtroppo non sono un esperto e non riesco più a correggerli.
Il mio codice è abbastanza impasticciato mi sà. Come IDE sto usando eclipse. Ti passerei anche tutto il progetto per vedere se è fattibile la cosa ma non vorrei disturbarti troppo, è un po scocciante leggere il codice altrui, dimmi tu. EDIT: Visto il video, molto elegante come soluzione |
|
|
|
|
|
#12 |
|
Senior Member
Iscritto dal: Nov 2004
Messaggi: 691
|
Ci sarebbe da smazzare un po tra progetti diversi per i due ide...l'ho convertito in normale swingworker, prova così;
Codice:
private class PopolaOntologiaTaskTask extends SwingWorker<Boolean, Void> implements Observer {
private final int contatore;
private final Worker worker = new Worker();;
private final JProgressBar progressBar;
private final JFrame frame = new JFrame();
PopolaOntologiaTaskTask(int contatore) {
// Runs on the EDT. Copy GUI state that
// doInBackground() depends on from parameters
// to PopolaOntologiaTaskTask fields, here.
this.contatore=contatore;
this.progressBar=new JProgressBar();
this.progressBar.setValue(0);
this.progressBar.setStringPainted(true);
this.progressBar.setMinimum(0);
this.progressBar.setMaximum(this.contatore);
this.frame.add(this.progressBar);
this.frame.setLocationRelativeTo(null);
this.frame.pack();
this.frame.setVisible(true);
}
@Override public Boolean doInBackground() {
// Your Task's code here. This method runs
// on a background thread, so don't reference
// the Swing GUI from here.
this.worker.addObserver(this);
return this.worker.doWork(this.contatore);// return your result
}
@Override public void done() {
try {
this.frame.dispose();
// Runs on the EDT. Update the GUI based on
// the result computed by doInBackground().
if (this.get() == true) {
JOptionPane.showMessageDialog(null, "TUTTO OK", "CONGRATULAZIONI", JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(null, "KO", "DANNAZIONE", JOptionPane.ERROR_MESSAGE);
}
} catch (InterruptedException ex) {
} catch (ExecutionException ex) {
}
}
public void update(Observable o, Object arg) {
this.progressBar.setValue(Integer.parseInt(arg.toString()));
}
}
|
|
|
|
|
|
#13 |
|
Member
Iscritto dal: Jan 2007
Messaggi: 173
|
niente, mi segna errore sulle linee contenenti Worker sia nella zona globale sia nel metodo doInBrackground() e non posso provarlo.
Inoltre mi comporta un errore qui Codice:
JButton sfogliaFashion = new JButton("Sfoglia", new ImageIcon("image/iconAccess.jpg"));
sfogliaFashion.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
int returnVal = fileChooser.showOpenDialog(createGUI.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fileChooser.getSelectedFile();
setPathFashion(file.getPath());
textFieldFashion.setText(file.getPath());
if ((getPathOntology() != null) && (getPathMondrian() != null)){
b.setEnabled(true);
}
}
}
});
p1.add(sfogliaFashion);
int returnVal = fileChooser.showOpenDialog(createGUI.this); che non riesco a correggere (createGui è il nome della mia classe) EDIT: Ma worker è la classe che fa il mio lavoro lungo? EDIT2: Ok mi resta l'errore sul fileChooser per provare il tutto EDIT3: Ho tolto gli errori ma la barra non si muove, smanetto ancora un po Ultima modifica di k_mishima : 17-05-2011 alle 00:05. |
|
|
|
|
|
#14 | |
|
Senior Member
Iscritto dal: Nov 2004
Messaggi: 691
|
Quote:
3) Cerca di capire se il metodo update() del tuo Observer viene "sollecitato" quando l'Observable comunica che "è cambiato". |
|
|
|
|
|
|
#15 |
|
Member
Iscritto dal: Jan 2007
Messaggi: 173
|
funziona
ora nel mio metodo per impostare % da 0 a 100% devo mettere 100 volte l'istruzione che aumenta di 1 il contatore, giusto? changedState(conta++); Comunque grazie infinite, sei stato gentilissimo |
|
|
|
|
|
#16 |
|
Senior Member
Iscritto dal: Nov 2004
Messaggi: 691
|
Dipende da quello che puoi fare. Ad esempio, nel codice che ti ho postato facevo:
Codice:
public void update(Observable o, Object arg) {
this.progressBar.setValue(Integer.parseInt(arg.toString()));
}
Codice:
this.progressBar.setMinimum(0); this.progressBar.setMaximum(this.contatore); il numero 0 --> la progress bar resta a 0% il numero 1 --> siccome la progress bar sa che può assumere valori da 0 a 3 è sufficientemente intelligente da settarti il valore del progress a 33% il numero 2 --> segna 66% il numero 3 --> segna 100%. Questo è il caso facile. C'è un caso più complesso. Immagina che il tuo Worker.doWork() fa: Codice:
for(int i=0; i<100.000; i++){
// lavoro
this.changedState(i);
}
Setti la barra in modo che vada da 0 a 100. Codice:
this.jprogressBar = new JProgressBar(0, 100); Codice:
public void update(Observable o, Object arg) {
this.progressBar.setValue(Integer.parseInt(arg.toString()));
}
Codice:
// 100 / the number of combinations to test represents the percentage value of every single test this.percentage = (float)100/this.combinations; Codice:
setProgress((int)Math.min((Integer)arg*this.percentage/100, 100) Codice:
setProgress((int)Math.min((Integer)67.000*0.001/100, 100) Codice:
setProgress((int)Math.min(67, 100) Ultima modifica di tylerdurden83 : 17-05-2011 alle 00:51. |
|
|
|
|
|
#17 |
|
Member
Iscritto dal: Aug 2010
Messaggi: 138
|
postresti utilizzare gli exchanger.
|
|
|
|
|
|
#18 |
|
Member
Iscritto dal: Jan 2007
Messaggi: 173
|
Non mi è chiara una cosa, tu hai parlato di 100000 iterazioni e hai spiegato ottimamente cosa fare nel caso.
Io nel mio luuungo programma faccio sempre questo: Query su un db su una o al max due tabelle in join Per ogni risultato della query (while sul resultSet.next()) estraggo dati colonna x colonna e li inserisco in un individuo della mia ontologia (sto usando le api di protege, comunque se non le conosci non è importante ai fini della risposta) Dunque i while hanno innanzitutto un numero di operazione dipendente dal numero dei record estratti dalle query, inoltre a causa dei diversi risultati delle query, all'interno avrò un numero di operazioni molto diverse tra loro in ogni record. (differenti numeri di colonne, richiesta di manipolazione dei dati, necessità di cercare elementi già inseriti precedentemente nell'ontologia, necessità di creare individui non trovati appartenenti a un while già processato....) Quindi ad esempio un while potrebbe avere un'iterazione che ci mette 1ms, un altro un operazione che ci mette 0,01ms e così via. Non conoscendo il numero totale di operazioni (calcolabile al limite a fine esecuzione sommando le varie iterazioni dei while con un contatore globale) e non essendo omogenei i tempi d'esecuzione dei cicli, non so come procedere. Inoltre è possibile modificare leggermente il codice in modo che invii anche una stringa da usare per inserirla in una JTextArea? Grazie
Ultima modifica di k_mishima : 17-05-2011 alle 11:14. |
|
|
|
|
|
#19 | ||
|
Senior Member
Iscritto dal: Nov 2004
Messaggi: 691
|
Puoi costruirti una cosa del genere?
Codice:
public class QueryManager{
private ResultSet resSet;
public int lookup(...){
...
this.resSet = ...
resultSet.last();
int rowCount = resultSet.getRow();
return rowCount;
}
public int lavora(){
for(Riga riga : this.resSet){
// lavoro
// notifico
}
}
}
Quote:
Quote:
Codice:
import java.util.Observable;
public class Worker extends Observable {
public void doWork(){
for(int i=0; i<10; i++){
System.out.print("Working....");
Thread.sleep((int)(5000 * Math.random()));
this.changedState("Messaggio:"+i);
}
}
private void changedState(String nuovoStato) {
setChanged();
notifyObservers(nuovoStato);
}
}
Codice:
import java.util.Observable;
import java.util.Observer;
public class Launcher implements Observer {
public void update(Observable o, Object arg) {
// ho deciso che il formato delle notifiche inviatemi dall observable è "messaggio:id"
String[] messRicevuto = arg.toString().split(":");
System.out.println("Ho ricevuto il messaggio:"+messRicevuto[0]+" con id:"+messRicevuto[1]);
}
}
Ultima modifica di tylerdurden83 : 17-05-2011 alle 12:10. |
||
|
|
|
|
|
#20 |
|
Member
Iscritto dal: Jan 2007
Messaggi: 173
|
grazie, ora la barra funziona bene, non è lineare in tutti i punti ma nella maggior parte si e mi va comunque benissimo.
più tardi provo a fare come hai suggerito riguardo il passaggio di messaggi, ora sono occupato su un mio bug, se vuoi dare un occhio lo trovi qui http://www.hwupgrade.it/forum/showth...6#post35173676 EDIT: Funziona benissimo anche il passaggio di messaggio, mi ha risolto l'ennesimo problema grazie. Ultima modifica di k_mishima : 17-05-2011 alle 22:07. |
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 02:02.




















