Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

4409723

Suspended
Original poster
Jun 22, 2001
2,221
0
Hello all,

I'm just finishing up a simple gui program and I'm having a small problem.

The crux of my problem is this:

I have two buttons (which call corresponding methods):
Step: goes through a single iteration.
Run: runs through steps until other GUI intervention stops it.

Now, run has a while(true) situation and I want the run method to continue until the outside gui interaction. I have the action listeners and etc. all set up it's just when I click the run button I get 100% CPU and no GUI responsiveness. I'm assuming threads are the remedy to the problem but have little experience with them and would be grateful if somebody could suggest how the simple thread arrangement would be constructed.


[I guess in other words I'd like one thread to manage the GUI and buttons and one for the computation].

Many thanks for any help,

Wes
 

x704

macrumors regular
Apr 15, 2006
118
0
really the only way is to have a var like

if (Stop == true){
System.out.println("Opps, somebody must have pressed the stop button.");
break;
}

in the method that is doing the CPU stuff.
This is what I have in my run method:

if (AbruptStop){
break; // check to see if we pressed 'cancel'
}

if(Pause){
try{
BeforePauseTime = System.currentTimeMillis();

wait(); //Pause the program, resume with notifyAll();

AfterPauseTime = System.currentTimeMillis() - BeforePauseTime;
TotalPauseTime += AfterPauseTime;
} catch(InterruptedException e){}
}

then just change the vars to true with your button handeling method like this.

MyButton4.addActionListener(new ActionListener(){
// implement the cancel button here
public void actionPerformed(ActionEvent e){
if (HasThreadRun){
if (!CalculatePrime.DoneCalculating()){
if(!DidWeCancel){
CalculatePrime.StopProcess();
DidWeCancel = true;
MyTextArea.append("Action canceled.\n");

if(Pause)
MyButton5.setText("Pause");

Pause = false;
}
}
}
}
});
 

savar

macrumors 68000
Jun 6, 2003
1,950
0
District of Columbia
Like the other guy said, changing while(true) to something like while(running) and make 'running' a boolean variable is probably a better idea. The GUI dispatches events on its own thread, so you're actually already running two threads. You just don't have a way for your thread to get the message.

If you're interested in thread still, though, the easiest way is to write a new class that implements the Runnable interface. You write the code you want the thread to execute in a single method run().

class MyThread implements Runnable {
public void run() {
while (true)
// do stuff
}
}

class MyTest {
public static void main (String args[]) {
(new Thread(new MyThread())).start();
}
}

Google for it. If you can't figure it out post back.
 

4409723

Suspended
Original poster
Jun 22, 2001
2,221
0
Okay guys,

It works fine now but I have one small change to completely finish this.

When it runs the updates are insanely fast so I need to tell the thread to sleep for a little bit in between each iteration. I've set up a Jslider to do this and monitor when this changes, but can't get sleep to use thread.sleep or whatever to achieve this. Any advice?

Cheers,

Wes
 

Compile 'em all

macrumors 601
Apr 6, 2005
4,131
359
Wes said:
Okay guys,

It works fine now but I have one small change to completely finish this.

When it runs the updates are insanely fast so I need to tell the thread to sleep for a little bit in between each iteration. I've set up a Jslider to do this and monitor when this changes, but can't get sleep to use thread.sleep or whatever to achieve this. Any advice?

Cheers,

Wes

The method to use is sleep() which is static so you can just type in
Thread.sleep(xxx) where xxx is time in ms. If you want your thread to sleep
for 5 mins, you do Thread.sleep(5*60*1000).

Remember that, after the sleep period is over your thread is not guaranteed
to run straight away. This is particularly true if you have several threads
running. when a thread wakes up its status will be changed to "runnable" and
not "running".
 

rtharper

macrumors regular
Sep 6, 2006
201
0
Oxford, UK
Wes said:
Okay guys,

It works fine now but I have one small change to completely finish this.

When it runs the updates are insanely fast so I need to tell the thread to sleep for a little bit in between each iteration. I've set up a Jslider to do this and monitor when this changes, but can't get sleep to use thread.sleep or whatever to achieve this. Any advice?

Cheers,

Wes


You could also use a Semaphore or a Mutex to make sure that each iteration only starts and stops when the other work being done is finished. This is the only provably correct method (a semaphore/lock/mutex) to synchronize your threads. Calling sleep is still non-deterministic because the thread sleeps for x seconds only after it is active again the process queue, and as said before when it wakes it is not guaranteed to immediately run.

It's a general rule of thumb for concurrent systems to never used hard-coded times. It's horribly bad practice.
 

4409723

Suspended
Original poster
Jun 22, 2001
2,221
0
Compile 'em all said:
The method to use is sleep() which is static so you can just type in
Thread.sleep(xxx) where xxx is time in ms. If you want your thread to sleep
for 5 mins, you do Thread.sleep(5*60*1000).

Remember that, after the sleep period is over your thread is not guaranteed
to run straight away. This is particularly true if you have several threads
running. when a thread wakes up its status will be changed to "runnable" and
not "running".


Works perfectly, thanks a lot!

rtharper said:
You could also use a Semaphore or a Mutex to make sure that each iteration only starts and stops when the other work being done is finished. This is the only provably correct method (a semaphore/lock/mutex) to synchronize your threads. Calling sleep is still non-deterministic because the thread sleeps for x seconds only after it is active again the process queue, and as said before when it wakes it is not guaranteed to immediately run.

It's a general rule of thumb for concurrent systems to never used hard-coded times. It's horribly bad practice.

Point taken, but in a worst case scenario what could happen in my program? The GUI not update in sync with the operation? Or something more drastic?

I do agree that in a much more larger program with less margin for error I would definitely go down the path of semaphores and etc.

Thanks once again,

Wes
 

rtharper

macrumors regular
Sep 6, 2006
201
0
Oxford, UK
Wes said:
Works perfectly, thanks a lot!



Point taken, but in a worst case scenario what could happen in my program? The GUI not update in sync with the operation? Or something more drastic?

I do agree that in a much more larger program with less margin for error I would definitely go down the path of semaphores and etc.

Thanks once again,

Wes

In this case, probably. You could also get some erroneous values in your GUI because the computation could be swapped out mid-iteration and the GUI would try to update. The problem with concurrent threads is that you just don't really know; you can test it 100 times but the 101st could produce the error you're looking for.

The java classes Semaphore and Lock will do pretty much what you need with almost no extra work at all.
 

Compile 'em all

macrumors 601
Apr 6, 2005
4,131
359
If you are interested in synchronizing threads (i.e starting a thread after another finishes)
then take a look at the join() method. For example,

Thread t = new Thread();
t.start();
t.join();

will result in the "current" thread starting after thread t finishes.
 

4409723

Suspended
Original poster
Jun 22, 2001
2,221
0
rtharper said:
In this case, probably. You could also get some erroneous values in your GUI because the computation could be swapped out mid-iteration and the GUI would try to update. The problem with concurrent threads is that you just don't really know; you can test it 100 times but the 101st could produce the error you're looking for.

The java classes Semaphore and Lock will do pretty much what you need with almost no extra work at all.

I'd really like to do this with semaphores and after thinking about it I see how it work I have one concern. I need the iteration after a specified time in the GUI and I don't see how I can do this without the threads.
 

MarkCollette

macrumors 68000
Mar 6, 2003
1,559
36
Toronto, Canada
I mostly agree with everyone's advice, but have a few extra points.

- Don't use Thread.sleep(). Instead use Object.wait() and Object.notify()

- Make sure you're doing all of your calculations in the worker thread, whether the user clicked Run or Step.

- If you have to update the GUI to show results, or do visualisations, then don't simply do it from the worker thread. The appropriate way is to package up the info into some object that implements Runnable, and pass that to javax.swing.SwingUtilities.invokeLater(Runnable). That will make it so that the GUI thread calls that object's run() method, at which time it can use the data it has to update your GUI.

- Recognise that there's a flow of execution:

1. The user clicks, and that's processed in the GUI thread.
2. An ActionListener sets a variable, and notifies the worker thread.
3. The worker thread wakes up, checks that field, and postentially does some work.
4. When the work is completed, the results are bundled up and invokedLater.
5. The GUI thread wakes up and executes the Runnable.

At the point of execution of the Runnable, updating the GUI may or may not still be applicable, depending on which user interactions have happenned since step 1. For example, the user may have close the window, or clicked Cancel, or switched to a different visualisation view, or whatever. Just be aware at each hand-off point, that the situation might have already changed. That's why it's important for there to be one single point where state is maintained, that supercedes all others. Let's look at it again:

1. The user clicks, and that's processed in the GUI thread.
2. An ActionListener sets a variable, and notifies the worker thread.
// In a synchronised block
GlobalState = RUNNING
3. The worker thread wakes up, checks that field, and postentially does some work.
// In a synchronised block
WorkerThread.LocalState = GlobalState
4. When the work is completed, the results are bundled up and invokedLater.
// If there's heavy processing, might want to see if should bother
// bundling results. But even then, you can have situations where
// LocalState == STEP and GlobalState == STOP, but we still want
// to see the results of stepping
5. The GUI thread wakes up and executes the Runnable.
// Definitely have to check GlobalState to see if should use results
// since could have changed modes or whatever. Remember, it
// was an optimisation to check at step 4, but it's imperative here.

So, notice how we actually store state in two places, but the GlobalState has precedence? And LocalState is really just a temporary working state for the worker thread.
 

rtharper

macrumors regular
Sep 6, 2006
201
0
Oxford, UK
MarkCollette said:
I mostly agree with everyone's advice, but have a few extra points.

- Don't use Thread.sleep(). Instead use Object.wait() and Object.notify()

Careful with notify, it can have a non-deterministic behavior in multi-threaded environments. However, those are essentially analogous to using Semaphores in terms of behvaior, and semaphores tend to be a more elegant solution, imho.
 

MarkCollette

macrumors 68000
Mar 6, 2003
1,559
36
Toronto, Canada
rtharper said:
Careful with notify, it can have a non-deterministic behavior in multi-threaded environments. However, those are essentially analogous to using Semaphores in terms of behvaior, and semaphores tend to be a more elegant solution, imho.

Do you mean using Object.notify() versus Object.notifyAll() ?
I usually use notifyAll(). I think that notify() is usually only used as an optimisation, when you really only want one Thread to wake up, and don't care which.
 

rtharper

macrumors regular
Sep 6, 2006
201
0
Oxford, UK
MarkCollette said:
Do you mean using Object.notify() versus Object.notifyAll() ?
I usually use notifyAll(). I think that notify() is usually only used as an optimisation, when you really only want one Thread to wake up, and don't care which.

Yeah, that's what I was saying =) Probably the most evil part of concurrent programming in Java is that you either a) wake up a random thread or b) you have to wake up all the threads. Imagine if OS thread/process libraries were written that way...

I have to do a lot of message passing parallel stuff in Java right now. After looking at other options that have been used, I have to say the Java thread library is one of the most horribly conceived ideas ever.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.