Link to home
Start Free TrialLog in
Avatar of Dawkins
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(simulationEcology);
      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
  }
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

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
Avatar of krispin
krispin

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'
Avatar of Dawkins

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(?).
Are you sure that the gui has been allowed to fully show itself in its initial state before the simulation thread has been started?
Avatar of Dawkins

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?
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.
Avatar of Dawkins

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.invokeLater(new Runnable() {
      public void run() {
        createAndShowGUI();
      }
    });
  }

The Gui created by createAndShowGUI() is the one which then creates the simulation thread.
Avatar of Dawkins

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..
SOLUTION
Avatar of lcwiding
lcwiding

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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*.
Avatar of Dawkins

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.getCurrentGeneration() < ecology.getNumberOfGenerations() && !isStopped && !isCancelled) {
    ....................... lots of calculations
    Thread.yield() ;
    }
}
Avatar of Dawkins

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 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
>> 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.invokeLater(new Runnable() {
      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
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Dawkins

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.getCurrentGeneration() < ecology.getNumberOfGenerations() && !isStopped && !isCancelled) {
      if( (this.getPriority() != threadPriority) ){
        this.setPriority(threadPriority);
      }

      ecology.playGeneration();
      progressBar.setValue(ecology.getCurrentGeneration());
      progressBar.setString("Generation: "+ecology.getCurrentGeneration()+" / "+ecology.getNumberOfGenerations());
      lastCheckedTime = System.currentTimeMillis();
      long timeTakenToDoChunk = lastCheckedTime - startTime;
      float progress = (float)ecology.getNumberOfGenerations() / (float)ecology.getCurrentGeneration();
      long estimatedTotalTime = Math.round(progress * timeTakenToDoChunk);

      timeRemainingLabel.setText(TIME_REMAINING_TXT +
                                 millisecondsToTime(estimatedTotalTime - timeTakenToDoChunk));
      while (isPaused) {
        synchronized (this) {
          try {
            wait();
          }
          catch (InterruptedException ex) {
          }
        }
      }
      Thread.yield();
    }
    this.setPriority(NORM_PRIORITY);
    if(isCancelled != true) {
      ecology.setNumberOfGenerations(ecology.getCurrentGeneration());
      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.
Don't forget to place any signal to the gui, having received any events on that listener, on the event dispatch thread
Avatar of Dawkins

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(SimulationListener gui) {}
public void run() {
}
}


Avatar of Dawkins

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.invokeLater(new Runnable() {
        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
Avatar of Dawkins

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?)
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
> 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.
Avatar of Dawkins

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.invokeLater(new Runnable() {
        public void run() {
            simListener.progress(timeLeft);
        }
     }
--------/code---------
Avatar of Dawkins

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?
>  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


8-)