Dawkins
asked on
Need help with muti thread gui
Hi all,
I have a Gui which launches a simulation which runs in its own thread. While the simulation is running, it displays a Gui showing its progress. My problem is that because the simulation is extremely processor intensive the Gui displaying its progress takes ages to show itself.
Here is the code, can anyone tell me any structural mistake I am making?
// Here is how it is called from the main GUI:
Simulation simulation = new Simulation(simulationEcolo gy);
simulation.start();
// And here is class Simulation:
public class Simulation extends Thread implements ActionListener {
public Simulation(Ecology ecology) {
this.ecology = ecology;
createAndShowGUI(); // creates a GUI displaying the simulation's progress.
}
public void run() {
runSimulation(); // Very processor intensive
}
I have a Gui which launches a simulation which runs in its own thread. While the simulation is running, it displays a Gui showing its progress. My problem is that because the simulation is extremely processor intensive the Gui displaying its progress takes ages to show itself.
Here is the code, can anyone tell me any structural mistake I am making?
// Here is how it is called from the main GUI:
Simulation simulation = new Simulation(simulationEcolo
simulation.start();
// And here is class Simulation:
public class Simulation extends Thread implements ActionListener {
public Simulation(Ecology ecology) {
this.ecology = ecology;
createAndShowGUI(); // creates a GUI displaying the simulation's progress.
}
public void run() {
runSimulation(); // Very processor intensive
}
You need to ensure that any updates to the gui are made in the gui thread. In your case, in your run, you could do this by doing EventQueue.invokeAndWait, with a Runnable that updates the gui
I would imagine that if the method "runSimulation();" is processor intensive there may be no way around it other than trying to make you method more efficient. Try remove a few variables (and re-using others, etc). Without seeing the code for the method I'd be unsure of what else to suggest
>>in your run
Should really have said 'in your runSimulation'
Should really have said 'in your runSimulation'
ASKER
It isn't really *updates* to the Gui that are the problem - it's mainly just the fact that the Gui takes ages to *create* in the first place.
By setting the simulation thread's priority to lowest it cures the problem - but I want it to have as much processor as possible so that it finishes as quickly as possible.
So maybe the solution is some sort of hack which sets the priority to lowest to allow the Gui to form, then after an arbitrary amount of time it increases the priority? This doesn't seem like a good design to me though(?).
By setting the simulation thread's priority to lowest it cures the problem - but I want it to have as much processor as possible so that it finishes as quickly as possible.
So maybe the solution is some sort of hack which sets the priority to lowest to allow the Gui to form, then after an arbitrary amount of time it increases the priority? This doesn't seem like a good design to me though(?).
Are you sure that the gui has been allowed to fully show itself in its initial state before the simulation thread has been started?
ASKER
> Are you sure that the gui has been allowed to fully show itself in its initial state before the simulation thread has been started?
Nope that sounds like exactly the problem. How do I check this or allow for it?
Nope that sounds like exactly the problem. How do I check this or allow for it?
Without seeing the code for your main thread, it is hard to be more specific. But, one issue to consider is that Java uses a cooperative threading model, so other threads will not get time unless a thread gives the a chance. If your simulation thread is computational intense, I would recommend interspersing some calls to Thread.yield() within your loop to provide opportunities for other threads to jump in and run.
If it is just the initial display that you want to make sure appears quickly, you could try setting up either an initial sleep call before starting your main thread so that the UI has time to initialize, or set a flag that your UI code will set once it has initialized. Your simulaiton thread could set a wait on that flag, which the UI flag could then issue a notify to reawaken the simulation thread.
If it is just the initial display that you want to make sure appears quickly, you could try setting up either an initial sleep call before starting your main thread so that the UI has time to initialize, or set a flag that your UI code will set once it has initialized. Your simulaiton thread could set a wait on that flag, which the UI flag could then issue a notify to reawaken the simulation thread.
ASKER
> or set a flag that your UI code will set once it has initialized
I tried this by changing a flag after the method createAndShowGui had finished, but it didn't work. Even though my code had finished executing, the gui still hadn't appeared on screen. My novice speculation is that although my setup code had finished, a separate thread actually handles the displaying of the graphics on the screen, and this was having trouble getting enough processor time?
If it helps, here is the main method:
public static void main(String arguments[]) {
javax.swing.SwingUtilities .invokeLat er(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
The Gui created by createAndShowGUI() is the one which then creates the simulation thread.
I tried this by changing a flag after the method createAndShowGui had finished, but it didn't work. Even though my code had finished executing, the gui still hadn't appeared on screen. My novice speculation is that although my setup code had finished, a separate thread actually handles the displaying of the graphics on the screen, and this was having trouble getting enough processor time?
If it helps, here is the main method:
public static void main(String arguments[]) {
javax.swing.SwingUtilities
public void run() {
createAndShowGUI();
}
});
}
The Gui created by createAndShowGUI() is the one which then creates the simulation thread.
ASKER
Another point I should make - the reason this is such a problem is that I am setting the priority for the processor intensive thread to Thread.MAX_PRIORITY -1
I'm doing this because I want the simulation to run as fast as possible.. but maybe it isn't such a good idea..
I'm doing this because I want the simulation to run as fast as possible.. but maybe it isn't such a good idea..
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
You should create the gui on the main application thread
>>is that I am setting the priority for the processor intensive thread to Thread.MAX_PRIORITY -1
That's probably not such a good idea *at this point*.
>>is that I am setting the priority for the processor intensive thread to Thread.MAX_PRIORITY -1
That's probably not such a good idea *at this point*.
ASKER
ok I now create the GUI from within the simulation thread and put a sleep in as you recommended and it seems to work:
public void run() {
createAndShowGUI();
try {
Thread.sleep(100);
}
catch (InterruptedException ex) {
}
runSimulation();
}
I've also put Thread.yield() into the simulation loop :
private void runSimulation() {
startTime = System.currentTimeMillis() ;
while (ecology.getCurrentGenerat ion() < ecology.getNumberOfGenerat ions() && !isStopped && !isCancelled) {
....................... lots of calculations
Thread.yield() ;
}
}
public void run() {
createAndShowGUI();
try {
Thread.sleep(100);
}
catch (InterruptedException ex) {
}
runSimulation();
}
I've also put Thread.yield() into the simulation loop :
private void runSimulation() {
startTime = System.currentTimeMillis()
while (ecology.getCurrentGenerat
....................... lots of calculations
Thread.yield() ;
}
}
ASKER
> You should create the gui on the main application thread
I did have it like that but changed it after reading the tutorial where it says to put it into an invokelater when using the latest SDK. I can't find the exact quote but this alludes to it:
http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html
"You have probably noticed that most of the tutorial's demos use a standardized main method that calls the SwingUtilities method invokeLater to ensure that the GUI is created on the event-dispatching thread. "
I did have it like that but changed it after reading the tutorial where it says to put it into an invokelater when using the latest SDK. I can't find the exact quote but this alludes to it:
http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html
"You have probably noticed that most of the tutorial's demos use a standardized main method that calls the SwingUtilities method invokeLater to ensure that the GUI is created on the event-dispatching thread. "
>>. I can't find the exact quote but this alludes to it
You're right - this is what they're saying:
"To avoid the possibility of thread problems, we recommend that you use invokeLater to create the GUI on the event-dispatching thread for all new applications"
That's a great pity, and another nail in the coffin for client-side Java
You're right - this is what they're saying:
"To avoid the possibility of thread problems, we recommend that you use invokeLater to create the GUI on the event-dispatching thread for all new applications"
That's a great pity, and another nail in the coffin for client-side Java
>> invokeLater
This is, IMHO, another reason to avoid Swing. But that is another discussion.
One thought on when you were using a flag to indicate that the UI had been generated, how did you implement that? My recommendation would be something like:
public Object uiWait = new Object();
public static void main(String arguments[]) {
javax.swing.SwingUtilities .invokeLat er(new Runnable() {
public void run() {
createAndShowGUI();
uiWait.notifyAll();
}
});
public void run() {
try {
uiWait.wait();
Thread.sleep(100);
}
catch (InterruptedException ex) {
}
runSimulation();
}
This is, IMHO, another reason to avoid Swing. But that is another discussion.
One thought on when you were using a flag to indicate that the UI had been generated, how did you implement that? My recommendation would be something like:
public Object uiWait = new Object();
public static void main(String arguments[]) {
javax.swing.SwingUtilities
public void run() {
createAndShowGUI();
uiWait.notifyAll();
}
});
public void run() {
try {
uiWait.wait();
Thread.sleep(100);
}
catch (InterruptedException ex) {
}
runSimulation();
}
Use of the SwingWorker class mentioned in the above link is useful for running code in a seperate thread.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
ok thanks for the help so far. I've increased the points because I want to get into this in more detail!
-----
>>From a design perspective though, the implementation of your simulation and your gui should be seperate and thus minimising the types of problems you are running into. So your simulation should not worry about updating the gui, and instead the gui updates according to the current state of the sim.
-----
Hmm I don't understand how to do this. My code for the simulation loop is below. It updates the gui every iteration of the simulation loop. It also can be paused via the Gui and have it's thread priority changed by the gui. To do what you are suggesting wouldn't I need a separate loop running in the Gui thread to check for values set by the simulation thread?
private void runSimulation() {
startTime = System.currentTimeMillis() ;
while (ecology.getCurrentGenerat ion() < ecology.getNumberOfGenerat ions() && !isStopped && !isCancelled) {
if( (this.getPriority() != threadPriority) ){
this.setPriority(threadPri ority);
}
ecology.playGeneration();
progressBar.setValue(ecolo gy.getCurr entGenerat ion());
progressBar.setString("Gen eration: "+ecology.getCurrentGenera tion()+" / "+ecology.getNumberOfGener ations());
lastCheckedTime = System.currentTimeMillis() ;
long timeTakenToDoChunk = lastCheckedTime - startTime;
float progress = (float)ecology.getNumberOf Generation s() / (float)ecology.getCurrentG eneration( );
long estimatedTotalTime = Math.round(progress * timeTakenToDoChunk);
timeRemainingLabel.setText (TIME_REMA INING_TXT +
millisecondsToTime(estimat edTotalTim e - timeTakenToDoChunk));
while (isPaused) {
synchronized (this) {
try {
wait();
}
catch (InterruptedException ex) {
}
}
}
Thread.yield();
}
this.setPriority(NORM_PRIO RITY);
if(isCancelled != true) {
ecology.setNumberOfGenerat ions(ecolo gy.getCurr entGenerat ion());
Sounds.playEnd();
ViewResultsFrame resultsGUI = new ViewResultsFrame(ecology);
resultsGUI.show();
}
frame.dispose();
}
-----
>>From a design perspective though, the implementation of your simulation and your gui should be seperate and thus minimising the types of problems you are running into. So your simulation should not worry about updating the gui, and instead the gui updates according to the current state of the sim.
-----
Hmm I don't understand how to do this. My code for the simulation loop is below. It updates the gui every iteration of the simulation loop. It also can be paused via the Gui and have it's thread priority changed by the gui. To do what you are suggesting wouldn't I need a separate loop running in the Gui thread to check for values set by the simulation thread?
private void runSimulation() {
startTime = System.currentTimeMillis()
while (ecology.getCurrentGenerat
if( (this.getPriority() != threadPriority) ){
this.setPriority(threadPri
}
ecology.playGeneration();
progressBar.setValue(ecolo
progressBar.setString("Gen
lastCheckedTime = System.currentTimeMillis()
long timeTakenToDoChunk = lastCheckedTime - startTime;
float progress = (float)ecology.getNumberOf
long estimatedTotalTime = Math.round(progress * timeTakenToDoChunk);
timeRemainingLabel.setText
millisecondsToTime(estimat
while (isPaused) {
synchronized (this) {
try {
wait();
}
catch (InterruptedException ex) {
}
}
}
Thread.yield();
}
this.setPriority(NORM_PRIO
if(isCancelled != true) {
ecology.setNumberOfGenerat
Sounds.playEnd();
ViewResultsFrame resultsGUI = new ViewResultsFrame(ecology);
resultsGUI.show();
}
frame.dispose();
}
Define a listener that includes methods required to notify of events of interest during the simulation.
For example something like:
public interface SimulationListener
{
public void progress(Simulation sim, Ecology eco, long timeTaken, long totalTime);
public void paused(Simulation sim, Ecology eco);
public void resumed(Simulation sim, Ecology eco);
public void loop(Simulation sim, Ecology eco);
}
The your sim class would then call the listener(s), instead of updating the gui.
And you would have a listener that would handle updating the gui, removing any coupling between the sim and the gui.
For example something like:
public interface SimulationListener
{
public void progress(Simulation sim, Ecology eco, long timeTaken, long totalTime);
public void paused(Simulation sim, Ecology eco);
public void resumed(Simulation sim, Ecology eco);
public void loop(Simulation sim, Ecology eco);
}
The your sim class would then call the listener(s), instead of updating the gui.
And you would have a listener that would handle updating the gui, removing any coupling between the sim and the gui.
Don't forget to place any signal to the gui, having received any events on that listener, on the event dispatch thread
ASKER
Ok, before I try this could you just check I'm on the right track? Am I right in thinking I would have a new class like this:
Class SimulationGUI implements SimulationListener
And then pass a reference to the SimulationListener interface into the Simulation class like this:
Class Simulation extends Thread {
public Simulation(SimulationListe ner gui) {}
public void run() {
}
}
Class SimulationGUI implements SimulationListener
And then pass a reference to the SimulationListener interface into the Simulation class like this:
Class Simulation extends Thread {
public Simulation(SimulationListe
public void run() {
}
}
ASKER
(Didn't mean to submit... )
Then every loop in the Simulation I put an event on the event dispatch thread like this:
javax.swing.SwingUtilities .invokeLat er(new Runnable() {
public void run() {
gui.progress(Simulation sim, Ecology eco, long timeTaken, long totalTime);
}
});
Then every loop in the Simulation I put an event on the event dispatch thread like this:
javax.swing.SwingUtilities
public void run() {
gui.progress(Simulation sim, Ecology eco, long timeTaken, long totalTime);
}
});
The Simulation class should have an addListener method, just as with guis and addActionListener
invokeLater would be used to do the actual update to a gui
ASKER
> The Simulation class should have an addListener method, just as with guis and addActionListener
Surely the simulation *gui* should have the listener shouldn't it? Because it's listening for changes in the simulation loop?
I'm getting more confused here :). Would someone be able to just give a 1-2-3 set of steps for separating the Gui out of the simulation? I'm not even sure of step 1:
1) Create a new class SimulationGUI. Put all gui creation stuff in here (including event handlers for the pause button? Or do I put the button event handlers into the Simulation class?)
Surely the simulation *gui* should have the listener shouldn't it? Because it's listening for changes in the simulation loop?
I'm getting more confused here :). Would someone be able to just give a 1-2-3 set of steps for separating the Gui out of the simulation? I'm not even sure of step 1:
1) Create a new class SimulationGUI. Put all gui creation stuff in here (including event handlers for the pause button? Or do I put the button event handlers into the Simulation class?)
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
> Surely the simulation *gui* should have the listener shouldn't it?
no it *is* the listener, ie. your gui (or whatever class is used to control it) implements SimulationListener
> Would someone be able to just give a 1-2-3 set of steps for separating the Gui out of the simulation?
Create a Listener interface for your sim class, adding methods to add/remove listener (for simplicity you could initially just support a single listner). Also add code to fire the relevant event when they occur.
Create a listener implementation that updates the gui appropriately accordingly.
no it *is* the listener, ie. your gui (or whatever class is used to control it) implements SimulationListener
> Would someone be able to just give a 1-2-3 set of steps for separating the Gui out of the simulation?
Create a Listener interface for your sim class, adding methods to add/remove listener (for simplicity you could initially just support a single listner). Also add code to fire the relevant event when they occur.
Create a listener implementation that updates the gui appropriately accordingly.
ASKER
ok I'm trying to implement your suggestions. I still have a major question about how the gui is going to send events *back* the other way to the Simulation (to pause it etc). But anyway, here's what I've done so far could you tell me if this is what you meant?:
1) There's now 3 classes to replace the single original class Simulation:
--------code-----------
Class Simulation extends Thread
Class SimulationFrame extends JFrame implements SimulationListener, ActionListener, ChangeListener, ItemListener
Interface SimulationListener
--------/code---------
2) I've added a the SimulationListener to the Simulation like this:
--------code-----------
Class Simulation extends Thread{
Ecology ecology;
SimulationListener simListener;
public Simulation(Ecology ecology, SimulationListener listener) {
this.ecology = ecology;
this.simListener = listener;
}
--------/code---------
3) Then I send events from within the run() method like this:
--------code-----------
public void run() {
while(.....) {
........... // run iteration
final long timeLeft = estimatedTotalTime - timeTakenToDoChunk;
javax.swing.SwingUtilities .invokeLat er(new Runnable() {
public void run() {
simListener.progress(timeL eft);
}
}
--------/code---------
1) There's now 3 classes to replace the single original class Simulation:
--------code-----------
Class Simulation extends Thread
Class SimulationFrame extends JFrame implements SimulationListener, ActionListener, ChangeListener, ItemListener
Interface SimulationListener
--------/code---------
2) I've added a the SimulationListener to the Simulation like this:
--------code-----------
Class Simulation extends Thread{
Ecology ecology;
SimulationListener simListener;
public Simulation(Ecology ecology, SimulationListener listener) {
this.ecology = ecology;
this.simListener = listener;
}
--------/code---------
3) Then I send events from within the run() method like this:
--------code-----------
public void run() {
while(.....) {
........... // run iteration
final long timeLeft = estimatedTotalTime - timeTakenToDoChunk;
javax.swing.SwingUtilities
public void run() {
simListener.progress(timeL
}
}
--------/code---------
ASKER
Well the above seems to work even if I have got the wrong end of the stick with the design!
But it only works one way. How do I get the Gui to send information back to the simulation?
My current guess is to do the reverse of the above: make a new interface called "SimulationGuiListener which the Simulation can implement with methods such as "pause()" and "changeThreadPriority()" and communicate via that.
Can anyone confirm this as being stupid?
But it only works one way. How do I get the Gui to send information back to the simulation?
My current guess is to do the reverse of the above: make a new interface called "SimulationGuiListener which the Simulation can implement with methods such as "pause()" and "changeThreadPriority()" and communicate via that.
Can anyone confirm this as being stupid?
> I still have a major question about how the gui is going to send events *back* the other way to the Simulation (to pause it etc).
You would simply call the Sum method that performs the required action
eg. if you want to pause call it's pause() method (or whatever it may be called)
no listener interface required here
You would simply call the Sum method that performs the required action
eg. if you want to pause call it's pause() method (or whatever it may be called)
no listener interface required here
8-)
(: