How to safely stop a thread

This is where you talk about the EV3 software itself, installation issues, and programming talk.

Moderators: roger, gloomyandy, skoehler

ralfb1105
New User
Posts: 12
Joined: Mon Jan 15, 2018 10:14 am

How to safely stop a thread

Postby ralfb1105 » Tue Feb 20, 2018 10:59 am

Hi,

I have a question on how to stop a thread.
First I will explain my code and what I'm trying to do ...
I have a vehicle (ForkLifter) which move forward and backward. When moving backward a tone should be played, like in the real life :D
Here are my code sequences .. not all, because the movement works fine ...

I have a class "MoveBackBeep" with the following contents:

Code: Select all

package com.lego.ev3.lejos;

import lejos.hardware.Sound;
import lejos.utility.Delay;

public class MoveBackBeep implements Runnable {
   public void run () {
      while (true) {
         Sound.playTone(800, 350, 20);
         Delay.msDelay(800);
      }
   }
}


In the main Class "ForkLifter" I have initialized the thread using this code:

Code: Select all

Runnable moveBackBeep = new MoveBackBeep();
Thread thread3 = new Thread(moveBackBeep);

Then I want simply want to:
- move forward
- play tone in a seperate thread
- move backward
- stop tone (thread)
- move forward
- play tone in a seperate thread
- move backward
- stop tone(thread)
Here is my code:

Code: Select all

middle.rotate(30);
middle.stop();
pilot.travel(300);
Delay.msDelay(500);
middle.rotateTo(0);
middle.stop();
thread3.start();
pilot.travel(-300);
thread3.interrupt();
pilot.travel(300);
Delay.msDelay(500);
middle.rotate(30);
middle.stop();
thread3.start();
pilot.travel(-300);
thread3.interrupt();
middle.rotateTo(0);
middle.stop();

I i start the program, the ForkLifter moves forward, starts the tone, move abckward, stops the tone, moves forward and then I got an exception when starting again the thread:

Code: Select all

ava.lang.IllegalThreadStateException
   at java.lang.Thread.start(Thread.java:705)
   at com.lego.ev3.lejos.ForkLifter.main(ForkLifter.java:128)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
   at java.lang.reflect.Method.invoke(Method.java:606)
   at lejos.internal.ev3.EV3Wrapper.invokeClass(EV3Wrapper.java:62)
   at lejos.internal.ev3.EV3Wrapper.main(EV3Wrapper.java:46)


I think it's because the thread didn't finished using "thread3.interrupt", but I don't know the way to stop the thread, because thread3.stop() is deprecated.

Can someone explain how I should stop the thread to make my code working?

Thanks in advance for your help!

Regards,

Ralf

ralfb1105
New User
Posts: 12
Joined: Mon Jan 15, 2018 10:14 am

Re: How to safely stop a thread

Postby ralfb1105 » Tue Feb 20, 2018 11:55 am

Hi,

after reading some articles I treid to modify the class "MoveBackBeep" which implements the Runnable interface in the following way:

Code: Select all

package com.lego.ev3.lejos;

import lejos.hardware.Sound;
import lejos.utility.Delay;

public class MoveBackBeep implements Runnable {
   
   public void run () {
      while (!Thread.currentThread().isInterrupted()) {
         try {
            Thread.sleep(1000);
         } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return;
         }
         Sound.playTone(800, 350, 20);
         Delay.msDelay(400);
      }
   }
}


Unfortunately I get the same ThreadException :x
Any ideas?

Regards,

Ralf

User avatar
gloomyandy
leJOS Team Member
Posts: 6022
Joined: Fri Sep 28, 2007 2:06 pm
Location: UK

Re: How to safely stop a thread

Postby gloomyandy » Tue Feb 20, 2018 1:00 pm

The exception you are getting is because you can only start a Thread object once. When it exits you can not restart it by calling start again, you need to create a new instance of Thread each time. You should also probably make sure that the original instance has actually stopped before you continue on so you should probably call the threads join method after you have called interrupt to stop it. Oh and why are you calling Thread.currentThread.interrupt(0 in your exception handler? That is almost certainly not a good idea!
leJOS news https://lejosnews.wordpress.com/

ralfb1105
New User
Posts: 12
Joined: Mon Jan 15, 2018 10:14 am

Re: How to safely stop a thread

Postby ralfb1105 » Tue Feb 20, 2018 3:14 pm

Hi,

Thanks a lot for your explanation. In the meantime I have changed my class "MoveBackBeep" in the way:

Code: Select all

package com.lego.ev3.lejos;

import lejos.hardware.Sound;
import lejos.utility.Delay;

public class MoveBackBeep implements Runnable {
   
   public void run () {
      while (true) {
         if (ForkLifter.moveBackBeepStart) {
            Sound.playTone(800, 350, 20);
            Delay.msDelay(200);
         }
      }
   }
}


and I have changed my main class to (extract):

Code: Select all

public static volatile boolean moveBackBeepStart = false;

Code: Select all

middle.rotate(30);
middle.stop();
pilot.travel(300);
Delay.msDelay(500);
middle.rotateTo(0);
middle.stop();
ForkLifter.moveBackBeepStart = true;
pilot.travel(-300);
ForkLifter.moveBackBeepStart = false;
pilot.travel(300);
Delay.msDelay(500);
middle.rotate(30);
middle.stop();
ForkLifter.moveBackBeepStart = true;
pilot.travel(-300);
ForkLifter.moveBackBeepStart = false;
middle.rotateTo(0);
middle.stop();


As I mentioned earlier in my posts, I'm a newbie with java programming, so I thought it might be not a good idea to start and stop threads, and it might be better practice to leave the thread running all the time and use a global variable to decide if we have to play the sound or not.

It would be great if you can teach me if my new code is OK or no.

Thanks a lot for all your effort and help!!

Regards,

Ralf

User avatar
gloomyandy
leJOS Team Member
Posts: 6022
Joined: Fri Sep 28, 2007 2:06 pm
Location: UK

Re: How to safely stop a thread

Postby gloomyandy » Tue Feb 20, 2018 5:23 pm

It will probably be fine, but what you have there is what is known as a busy/wait loop which will basically use the cpu all of the rime, there are much better ways to communicate between threads (take a look at notify/wait), at the very least you should probably but a short (say 20mS) sleep in there (you can use the leJOS msDelay method to do that if you want to avoid having try/catch around the sleep). This will stop your loop from hogging the cpu. But you really should spend some time learning how to program in Java, and knowing how to use threads is a very important part of that, be warned, using threads especially ones that share data, can be very tricky and you really need to understand what the issues are and how to deal with them.

There are lots of Java tutorials out there.
leJOS news https://lejosnews.wordpress.com/

ralfb1105
New User
Posts: 12
Joined: Mon Jan 15, 2018 10:14 am

Re: How to safely stop a thread

Postby ralfb1105 » Tue Feb 20, 2018 7:12 pm

Thanks a lot for your comments and your recommendations. You are right, I have to learn a lot in Java programming and I will try to do together with LeJOS. May I ask you if you know a good tutorial for this kind of Thread programming? There are a lot of tutorials available but there are also a lot of bad things in the Web and it‘s always very helpful to get some tips from a Guru :D

Have a nice evening!

Regards,

Ralf

User avatar
gloomyandy
leJOS Team Member
Posts: 6022
Joined: Fri Sep 28, 2007 2:06 pm
Location: UK

Re: How to safely stop a thread

Postby gloomyandy » Tue Feb 20, 2018 8:54 pm

The ones from Oracle are pretty good...
https://docs.oracle.com/javase/tutorial/

But there are lots out there, I'm sure you will find something that works for you.
leJOS news https://lejosnews.wordpress.com/

ralfb1105
New User
Posts: 12
Joined: Mon Jan 15, 2018 10:14 am

Re: How to safely stop a thread

Postby ralfb1105 » Fri Feb 23, 2018 4:05 pm

Hi,

Thanks a lot for the link. I have read a lot, ask some questions and finally I end up with a new implementation with wait() and notify().
Here are the code from the main class (extract)

Code: Select all

/*
 * Global variable to control Beep tone when moving backward and create an Object for synchronization
 */
public static volatile boolean moveBackBeepStart = false;
public static final Object lock = new Object();

When moving backward set the flag to true ...

Code: Select all

static void setFlag(boolean b) {
   moveBackBeepStart = b;
   synchronized (lock) {
   // Flag changed, notify the child ...
   lock.notify();
   }
}

public static void moveBack(Double distance) {
    pilot.setAngularSpeed(700);
   pilot.setAngularAcceleration(50);
   pilot.setLinearAcceleration(50);
   pilot.setLinearSpeed(1400);
   middle.setAcceleration(200);
   ForkLifter.setFlag(true);
   pilot.travel(-distance);
   ForkLifter.setFlag(false);
}

The MoveBackBeep Class implemented as a thread using Runnable, will be started in the main class using thread.start().

Code: Select all

package com.lego.ev3.lejos;

import lejos.hardware.Sound;
import lejos.utility.Delay;

public class MoveBackBeep implements Runnable {
   
   public void run () {
      try {
         while (true) {
            //get the Lock
            synchronized (ForkLifter.lock) {
               //Check the flag
               while (!ForkLifter.moveBackBeepStart) {
                  ForkLifter.lock.wait();
               }
               //wake upo and flag=true -> begin action
               Sound.playTone(800, 350, 10);
               Delay.msDelay(300);
            }
         }
      } catch (Exception ie) {
         ie.printStackTrace();
      }
   }
}


What do you think about this new implementation?
Maybe this will be useful for other user ;-)

Regards,

Ralf

User avatar
gloomyandy
leJOS Team Member
Posts: 6022
Joined: Fri Sep 28, 2007 2:06 pm
Location: UK

Re: How to safely stop a thread

Postby gloomyandy » Fri Feb 23, 2018 4:26 pm

Much better, but you should probably set the flag inside of the synchronized block, also it would be better to have methods called something like, startBeep and stopBeep (which can then call setFlag) rather than calling setFlag directly.
leJOS news https://lejosnews.wordpress.com/

ralfb1105
New User
Posts: 12
Joined: Mon Jan 15, 2018 10:14 am

Re: How to safely stop a thread

Postby ralfb1105 » Sat Feb 24, 2018 4:26 pm

Hi,

much better is not enough :D ... so I tried to implement your tips using extra methods, which works fine. Here are my result, hopefully you can review it another time:

Code: Select all

static void startBeep() {
          setFlag(true);
   }
   
   static void stopBeep() {
         setFlag(false);
   }
    
    static void setFlag(boolean b) {
       moveBackBeepStart = b;
       synchronized (lock) {
         // Flag hat sich ge‰ndert -> Kind benachrichtigen
         lock.notify();
       }
   }
    
    public static void moveBack(Double distance) {
       pilot.setAngularSpeed(700);
      pilot.setAngularAcceleration(50);
      pilot.setLinearAcceleration(50);
      pilot.setLinearSpeed(1400);
      middle.setAcceleration(200);
      startBeep();
      pilot.travel(-distance);
      stopBeep();
   }


With this change the program works as it works before. OK, now I tried to implement your 2nd tip "probably set the flag inside of the synchronized block".
I tried it but it didn't work, which means the beep begins but didn't stop as expected !!??

Here are the ways I tried it, probably not correct :(
1st idea:

Code: Select all

static void startBeep() {
   synchronized (lock) {
       setFlag(true);
   }       
}
   
static void stopBeep() {
   synchronized (lock) {
      setFlag(false);
   }
}
    
 static void setFlag(boolean b) {
    moveBackBeepStart = b;
   // Flag hat sich ge‰ndert -> Kind benachrichtigen
   lock.notify();
}


2nd idea:

Code: Select all

static void startBeep() {
    setFlag(true);
   }
   
static void stopBeep() {
   setFlag(false);
   }
    
 static void setFlag(boolean b) {
    synchronized (lock) {
       moveBackBeepStart = b;
      // Flag hat sich ge‰ndert -> Kind benachrichtigen
      lock.notify();
    }
}


It would be very helpful if you can teach me how I should implement your suggestion to put set the flag within the synchronized block.
At the end I would like to have a solution which ends up with a "Good" comment from your site :wink:

Thanks for all your help!!!!

Regards

Ralf

User avatar
gloomyandy
leJOS Team Member
Posts: 6022
Joined: Fri Sep 28, 2007 2:06 pm
Location: UK

Re: How to safely stop a thread

Postby gloomyandy » Sat Feb 24, 2018 8:13 pm

The final version were you set the boolean inside the setFlag method is probably best. You may want to modify your run method so that the code that plays the beep and sleeps is outside of the synchronized block. Doing that should allow the boolean to be set in a timely fashion.
leJOS news https://lejosnews.wordpress.com/

ralfb1105
New User
Posts: 12
Joined: Mon Jan 15, 2018 10:14 am

Re: How to safely stop a thread

Postby ralfb1105 » Mon Feb 26, 2018 10:58 am

Good catch - here are my next try - hopefully a little bit better than before :wink:
I have modified class "MoveBackBeep" -> removed the action from the synchronized block:

Code: Select all

package com.lego.ev3.lejos;

import lejos.hardware.Sound;
import lejos.utility.Delay;

public class MoveBackBeep implements Runnable {
   
   public void run () {
      try {
         while (true) {
            // get Lock
            synchronized (ForkLifter.lock) {
               //check flag
               while (!ForkLifter.moveBackBeepStart) {
                  ForkLifter.lock.wait();
               }
               //wake up and flag = true --> start your work ...
               Sound.playTone(800, 350, 10);
               Delay.msDelay(300);
            }
         }
      } catch (Exception ie) {
         ie.printStackTrace();
      }
   }
}


In the main class "ForkLifter" I have moved setting the boolean moveBackBeepStart into the synchronized block:

Code: Select all

static void startBeep() {
    setFlag(true);
}
   
static void stopBeep() {
   setFlag(false);
}
    
static void setFlag(boolean b) {
    synchronized (lock) {
       moveBackBeepStart = b;
      // Flag changed -> notify child
      lock.notify();
    }
}
 static void moveBack(Double distance) {
    setSpeed();
    //Fork must be min 15° up before moving
    if (middle.getTachoCount() < 15) {
       middle.rotateTo(15);
    }
   startBeep();
   pilot.travel(-distance);
   stopBeep();
}


What do you think about this new implementation?

Regards,

Ralf


Return to “EV3 Software”

Who is online

Users browsing this forum: No registered users and 1 guest