Link to home
Start Free TrialLog in
Avatar of drasin
drasin

asked on

Thread problem is ruining my life

If you can solve my problem then, in addition to points, you will have my eternal gratitude.
I am designing an applet which features moving graphics.
There is to be a "GameManager" class which will periodically
move all of the graphical objects according to their own
methods(I have this part down & it works).  The problem is
that the GameManager is supposed to dynamically alter the
behavior of the graphical object according to user input
(Mouse click events).  I have been unable to find a way to interrupt my infinite "move and wait" loop in such a way as to allow mouse clicks to be collected and processed in between moves. I am right now using the VisualAge for Java VM (not that it matters, since all VM's implementations are the same-right;-)?)  Anyway, the code I have looks something like this, to give you an idea:

Class GameScreen{
GameMangager gameManager;
...
...
public boolean handleEvent(Event evt ) {
// I am using this because I want my applet to be
// able to run on 1.02 browsers
if (evt.id      == Event.MOUSE_DOWN) {
              gameManager.processEvent(evt.x, evt.y);
      }
            return super.handleEvent(evt);
}      
public class GameManager implements Runnable{
Thread t;
...
...
public void processEvent{
...
...
}
private void move{
//contains all of the gory details
//of how to calculate movement of
//graphical objeces
...
...
}

public void run{
while(true){ //Infinite loop
move();
//[NEED METHOD HERE TO YIELD CONTROL UNTIL NEXT MOVEMENT
//SO THAT THE VM CAN PROCESS THE MOUSE CLICKS ANT INFORM
//THIS CLASS OF THEM.  EXPEREMENTING WITH A VERY LONG FOR
//LOOP HAS REVEALED THAT THE CLICKS ARE IN FACT NOTED, BUT
//DO NOT GET PROCESSED UNTIL THE LOOP ENDS.  I
//HAVE TRIED: t.sleep(), t.sleep(intervalBetweenMoves), //t.yield(), t.setPriority(Thread.PRIORITY_MIN), AND
//VARIOUS PERMUTATIONS.  IF ANY OF THESE METHODS ARE INDEED
//THE CORRECT ONES, THEN I AM NOT USING THEM CORRECTLY AND
//NEED TO KNOW THE RIGHT WAY TO USE THEM]
wait(intervalBetweenMoves);
}
Help appreciated!
Ben Drasin
Avatar of gwalters
gwalters

Here's what I usually do:


public void run()
{
  while (true) {
    switch (runAction) {
      case 0: //idle
        //you could do your movement here
        try {Thread.sleep(100);} catch (InterruptedException e){}
        break;

      case 1: //First action you want based on event
        //do the work
        runAction=0; //make idle again
        break;

      //more cases, as needed...
    }
  }
}

public boolean handleEvent(Event e)
{
  if (CONDITION1)
    mainAction=1;
  else if (CONDITION2)
    mainAction=2;
  else //...
  return super.handleEvent(e);
}


Avatar of drasin

ASKER

Tried it.  Does not work.  The main thread never even gets to set up the rest of the applet, because the selfish while loop never gives up control.  Sleep does not seem to be effective in this way; as I mentioned, I already tried this.  Is there anything else I need to be doing in order to get sleep to work? Or should I be using another method entirely?
Why not use a named thread, similar to the rejected answer;

while (thread.currentThread==t) {
  <do stuff>
}

You want your run method to execute the code within it for that thread, and it's locking because you are putting it into an endless loop. Your wait(intervalBetweenMoves) is then just t.sleep(intervalBetweenMoves). I know you said this didn't work but I think it should if you just ditch the while(true) loop you're getting stuck in.

I decided to add some code. It's primitive but it demonstrates what I'm getting at. Prints an ever incrementing number to standard output but will also report any events. I've marked it as an answer instead of a comment because if I think this should be what you need.



import java.applet.*;
import java.awt.*;

public class ThreadExample extends Applet implements Runnable{
      
      Thread            t;
      int                   i;
      
      public void start() {
            if (t==null) {
                  t=new Thread(this);
                  t.start();
            }
      }
      
      public void stop() {
            if (t!=null) {
                  t.stop();
                  t=null;
            }
      }
      
      public void run() {
            while (Thread.currentThread()==t) {
                  i++;
                  System.out.println(i);
                  try {
                        Thread.sleep(1000);
                  }
                  catch (InterruptedException e) {
                  }
            }
      }
      
      public boolean handleEvent(Event e){
            System.out.println(e);
            return super.handleEvent(e);
      }
}
Huh?  The "selfish while loop" gives up the CPU every time through the loop for 100ms.  

Maybe the sleep time isn't long enough (although 100ms has always worked fine for me).  

Or maybe you're allowing mainAction to stay non-zero, in which case the while does indeed become a tight loop.


Oops, of course I meant runAction
After re-reading your question, maybe the thing is that you don't want anything other than the sleep in "case 0".  Maybe your move() method is too costly to do every 100ms (and maybe it's not necessary until you get an event and set runAction to some non-zero value).

Anyway, the whole point is that you poll some variable in your thread that gets changed in the handleEvent thread.  How often you poll is important: too often and you load the CPU, too seldom and you get poor response to the event that set it.


Still no luck.  Let me fill you all in on what I have tried so far (I do appreciate your attempts, but regret that I cannot award points :-) ).
In my code for Gamescreen.handleEvent and GameManager.move() I
am System.out.println'ing the current time; this confirms that the events are not registering until after the loop exits (I have been alternating between the while loop and a long for loop).  In order to allow the applet to finish loading, I put an extra if statement in handleEvent to start the game on the first sucessful handling of the event.  Mabey I am just missing something which you assume I know.  I'raise the stakes in view of my increacing desperation. The following is raw, bare, naked code from my applet (well, almost):

From class gameScreen:
public boolean handleEvent(Event evt ) {
if (evt.id      == Event.MOUSE_DOWN) {
      System.out.println("Got Event: " + evt);
            if (gameStarted ==false){
                  startGame();
                  gameStarted = true;
      }      
              launchMissile(curBase, evt.x, evt.y);
            
      }
            return super.handleEvent(evt);
}      
public void launchMissile (int base, int x, int y ) {
      System.out.println("fired at " + System.currentTimeMillis());
      gameManager().addMissileStrike(new MissileStrike(0, 0 ,x, y, 5));
      return;
}
public void startGame( ) {
      System.out.println("Run, Will Robinson, run!");
      gameManager().thread().run();
}

And from class gameManager:
public void run() {
      System.out.println("GameManager running.");
      gameState = GAME_MOVING;
      loopAndMove();
}                              
public void  loopAndMove ( ) {      
      int a = 0;
      while(a < 500){                                             System.out.println(System.currentTimeMillis());
             System.out.println("loop# "+ a);
             a++;
             switch(gameState){//this was a nice touch,
                                 //gwalters, thank you!  Does
                                 //not solve my problem, though!
            case GAME_MOVING://Yes, I defined these ints.      
            System.out.println("About to move");
            move();                                                                                        gameState = GAME_IDLE;
                break;
                case GAME_IDLE:
            System.out.println("About to sleep");
            try{
               thread().sleep(delay);//tried various times.
            }// end try
            catch(InterruptedException e){}
              break;
            }//end switch            
      }//end while
}
When run, the consol reads:
Got Event: java.awt.Event[id=501,x=160,y=196,target=v1.GameScreen[canvas4,1,1,498x406]]
Run, Will Robinson, run!
886785708842
loop 0
886785709043
loop 1
886785709223
loop 2
886785709403
loop 3
886785709573
loop 4
...
...
loop 498
About to sleep
887041503538
loop 499
About to sleep
waiting
Got Event: java.awt.Event[id=501,x=202,y=257,target=v1.GameScreen[canvas0,1,1,498x406]]
Got Event: java.awt.Event[id=501,x=83,y=253,target=v1.GameScreen[canvas0,1,1,498x406]]
fired at 887041506012
Missile created at 887041506162
adding missile at 887041506242
there are now 1 missiles
this missile is aimed at 83 253
Got Event: java.awt.Event[id=501,x=249,y=133,target=v1.GameScreen[canvas0,1,1,498x406]]
fired at 887041507384
Missile created at 887041507534
adding missile at 887041507594
there are now 2 missiles
this missile is aimed at 249 133
Got Event: java.awt.Event[id=501,x=243,y=166,target=v1.GameScreen[canvas0,1,1,498x406]]
fired at 887041508716
Missile created at 887041508856
adding missile at 887041508926
there are now 3 missiles
this missile is aimed at 243 166

HELP!!
Avatar of drasin

ASKER

I couldn't make any sense out of:

gameManager().thread().run()


Assuming "gm" is the gameManager object, try
(new Thread(gm)).start()


In any case, call "start()" on a Thread object to get the "run()" method started.  Don't call "run()" directly.  The "start()" method returns immediately and starts a new thread.  Calling "run()" will block the current thread until it returns.

Anyway, as you can probably tell, ExpertsExchange mangles source code pretty bad.  If you want, you can send stuff to me at walters@ct.net


Here's the answer you asked me to post.
ASKER CERTIFIED SOLUTION
Avatar of gwalters
gwalters

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