Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Terminer un Thread.

9 réponses
Avatar
jp
Bonjour,

J'ai une question toute bête: Comment fait-on pour terminer un Thread?
J'emploie la méthode interrupt() mais le thread, qui contient une boucle
infinie du genre while(true){code}, se remet en marche tout seul juste
après...

Quelle est la méthode pour ne plus avoir ce thread en mémoire? Pour le
terminer comme un Object quelconque?

Voici le source de mon thread:

import com.pi4j.wiringpi.*;

/**
*
* @author jp
*/
public class ShroomThread extends Thread {

long ms_on, ms_off;
int pinmode;
//boolean isRunning = true;

ShroomThread( long on, long off, int pin ) {
this.ms_on = on;
this.ms_off = off;
this.pinmode = pin;
Gpio.pinMode( pinmode, Gpio.OUTPUT );
}

public void setGpioLow() {
Gpio.digitalWrite( pinmode, Gpio.LOW );
}
/**
*
*/
@Override
public void run() {

while ( true ) {
Gpio.digitalWrite( pinmode, Gpio.HIGH );
Gpio.delay( ms_on );
Gpio.digitalWrite( pinmode, Gpio.LOW );
Gpio.delay( ms_off );
}

}

}

Merci d'avance!

9 réponses

Avatar
Samuel DEVULDER
Le 13/09/2018 à 00:27, jp a écrit :
Quelle est la méthode pour ne plus avoir ce thread en mémoire? Pour le
terminer comme un Object quelconque?

La façon, propre de faire cela est d'utiliser un drapeau (booleen). Tu
remplace le while(true) par while(drapeau), et pour stopper tu passes le
drapeau à faux et envoie une interruption (interrupt()) au thread pour
le sortir d'un eventuel object.wait() ou autre appel d'entrée/sortie sur
lequel il serait coincé.
a+
sam.
Avatar
Yliur
Le Thu, 13 Sep 2018 00:41:19 +0200
Samuel DEVULDER a écrit :
Le 13/09/2018 à 00:27, jp a écrit :
Quelle est la méthode pour ne plus avoir ce thread en mémoire? Pour
le terminer comme un Object quelconque?

La façon, propre de faire cela est d'utiliser un drapeau (booleen).
Tu remplace le while(true) par while(drapeau), et pour stopper tu
passes le drapeau à faux et envoie une interruption (interrupt()) au
thread pour le sortir d'un eventuel object.wait() ou autre appel
d'entrée/sortie sur lequel il serait coincé.

La modification et la lecture de la valeur devraient également se
trouver dans des blocs synchronisés (par exemple des fonctions marquées
synchronized), pour être sûr que la valeur a bien été partagée entre
les fils d'exécution.
Avatar
jp
Le Thu, 13 Sep 2018 01:20:22 +0200, Yliur a écrit :
Le Thu, 13 Sep 2018 00:41:19 +0200 Samuel DEVULDER
a écrit :
La façon, propre de faire cela est d'utiliser un drapeau (booleen). Tu
remplace le while(true) par while(drapeau), et pour stopper tu passes
le drapeau à faux et envoie une interruption (interrupt()) au thread
pour le sortir d'un eventuel object.wait() ou autre appel
d'entrée/sortie sur lequel il serait coincé.

La modification et la lecture de la valeur devraient également se
trouver dans des blocs synchronisés (par exemple des fonctions marquées
synchronized), pour être sûr que la valeur a bien été partagée entre les
fils d'exécution.

Ok,merci! Si j'envoie une interruption interrupt() cela va stopper ma
boucle immédiatement? Parceque le bloc while(true){code} peut être très
long dans mon cas... Plusieurs minutes ou dizaines de minutes. Je
voudrais le terminer tout de suite et ne pas attendre que la valeur while
(condition) soit traitée.
Dans un bouquin on me conseille de faire:
try {
while(true){
if (interrupted()) return;
}
} catch (InterruptedExeption e){return;}
Je vais tester pour voir. Merci de vos réponses.
Avatar
Yliur
Le 13 Sep 2018 03:22:58 GMT
jp a écrit :
Le Thu, 13 Sep 2018 01:20:22 +0200, Yliur a écrit :
Le Thu, 13 Sep 2018 00:41:19 +0200 Samuel DEVULDER
a écrit :

La façon, propre de faire cela est d'utiliser un drapeau
(booleen). Tu remplace le while(true) par while(drapeau), et pour
stopper tu passes le drapeau à faux et envoie une interruption
(interrupt()) au thread pour le sortir d'un eventuel object.wait()
ou autre appel d'entrée/sortie sur lequel il serait coincé.

La modification et la lecture de la valeur devraient également se
trouver dans des blocs synchronisés (par exemple des fonctions
marquées synchronized), pour être sûr que la valeur a bien été
partagée entre les fils d'exécution.

Ok,merci! Si j'envoie une interruption interrupt() cela va stopper ma
boucle immédiatement? Parceque le bloc while(true){code} peut être
très long dans mon cas... Plusieurs minutes ou dizaines de minutes.
Je voudrais le terminer tout de suite et ne pas attendre que la
valeur while (condition) soit traitée.
Dans un bouquin on me conseille de faire:
try {
while(true){
if (interrupted()) return;
}
} catch (InterruptedExeption e){return;}
Je vais tester pour voir. Merci de vos réponses.

Les questions importantes à te poser pour choisir comment faire :
- Est-ce que tu veux couper ton fil d'exécution en pleine action,
vraiment n'importe quand ?
- Est-ce qu'il ne risque pas de laisser des données (visibles en
dehors du fil) dans un état incohérent ?
- Est-ce que le fil exécute une boucle dans laquelle il passe très
souvent et obligatoirement à un endroit donné du code ?
- Est-ce que le fil est parfois bloqué (appel à sleep pour un long
moment, attente d'une connexion réseau, ...) ?
Avatar
jp
Le Thu, 13 Sep 2018 07:31:48 +0200, Yliur a écrit :
Les questions importantes à te poser pour choisir comment faire :
- Est-ce que tu veux couper ton fil d'exécution en pleine action,
vraiment n'importe quand ?

Oui.
- Est-ce qu'il ne risque pas de laisser des données (visibles en
dehors du fil) dans un état incohérent ?

Je peux l'éviter car ce n'est pas un thread compliqué. Il n'y a qu'un
paramètre à prendre en compte. C'est un thread qui allume et éteint un
relai. Il suffit juste de le mettre sur off avant de terminer le thread.
- Est-ce que le fil exécute une boucle dans laquelle il passe très
souvent et obligatoirement à un endroit donné du code ?

Il n'exécute qu'une boucle...
- Est-ce que le fil est parfois bloqué (appel à sleep pour un long
moment, attente d'une connexion réseau, ...) ?

... mais qui est très longue. C'est pour ça que je n'ai pas envie
d'attendre que le test de la condition dans le while s'exécute. Je veux
terminer tout de suite et proprement.
Autre question: Si je fais Thread t = new Thread(); quelque part dans le
programme et que je fais autre part if(t.isAlive()){code qui éteint le
thread} est-ce que c'est correct et est-ce que ça génère une erreur si t
n'existe pas encore?
A+
Avatar
Yliur
Le 13 Sep 2018 22:42:46 GMT
jp a écrit :
Le Thu, 13 Sep 2018 07:31:48 +0200, Yliur a écrit :
- Est-ce que le fil exécute une boucle dans laquelle il passe
très souvent et obligatoirement à un endroit donné du code ?

Il n'exécute qu'une boucle...
- Est-ce que le fil est parfois bloqué (appel à sleep pour un
long moment, attente d'une connexion réseau, ...) ?

... mais qui est très longue. C'est pour ça que je n'ai pas envie
d'attendre que le test de la condition dans le while s'exécute. Je
veux terminer tout de suite et proprement.

Là l'élément important c'est est-ce que la boucle est très longue parce
qu'elle réalise un traitement actif (des instructions sont exécutées en
permanence) ou bien elle est longue parce que le fil est bloqué à
différents moment ?
Dans le premier cas c'est plus délicat, il faut trouver des moments où
le fil va vérifier si on lui demande de s'arrêter. Dans ce cas,
peut-on avoir une vue globale du traitement ?
Dans le deuxième cas, quelles sont les opérations bloquantes ? Il y a
des chances pour qu'elles répondent aux interruptions des fils
d'exécution via la mécanique de java (interrupt()).
Suivant ton cas, j'essaierai d'expliquer plus en détail ce que tu peux
faire. La doc plus complète sur le sujet se trouve ici, mais c'est un
peu touffu :) :
<https://docs.oracle.com/javase/6/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html&gt;
- Est-ce qu'il ne risque pas de laisser des données (visibles en
dehors du fil) dans un état incohérent ?

Je peux l'éviter car ce n'est pas un thread compliqué. Il n'y a qu'un
paramètre à prendre en compte. C'est un thread qui allume et éteint
un relai. Il suffit juste de le mettre sur off avant de terminer le
thread.

Il faudra bien faire attention quand même :
- Ne pas détruire le fil brutalement (avec Thread.stop(), qui de
toutes façons est obsolète).
- Utiliser un try/finally, pour être sûr que la finalisation soit
réalisée.
Autre question: Si je fais Thread t = new Thread(); quelque part dans
le programme et que je fais autre part if(t.isAlive()){code qui
éteint le thread} est-ce que c'est correct et est-ce que ça génère
une erreur si t n'existe pas encore?

Suivant ce que tu veux dire par "n'existe pas" :
- Si tu n'as pas initialisé la variable (elle vaut null) : non, tu
vas avoir une NullPointException.
- Si le fil n'a simplement pas été lancé (via start()) : oui,
d'après la doc (<https://docs.oracle.com/javase/6/docs/api/java/lang/Thread.html#isAlive()>)
(le fil est en vie s'il a été démarré mais n'est pas encore mort)
(note : j'ai donné des références à la doc de java 6, parce que c'est
ce que j'ai sous la main, mais consulter une doc plus à jour c'est
mieux ;) )
Avatar
jp
Le Fri, 14 Sep 2018 05:46:30 +0200, Yliur a écrit :
Là l'élément important c'est est-ce que la boucle est très longue parce
qu'elle réalise un traitement actif (des instructions sont exécutées en
permanence) ou bien elle est longue parce que le fil est bloqué à
différents moment ?

Il y a deux delay à l'intérieur d la boucle infinie qui peuvent durer
plusieurs minutes chacun.
Dans le deuxième cas, quelles sont les opérations bloquantes ? Il y a
des chances pour qu'elles répondent aux interruptions des fils
d'exécution via la mécanique de java (interrupt()).

Je ne sais pas... Je vais essayer de remplacer les delay, qui sont des
méthodes implémentées par l'API spécifique que j'utilise, par des
Thread.sleep(n); pour voir...
Suivant ton cas, j'essaierai d'expliquer plus en détail ce que tu peux
faire. La doc plus complète sur le sujet se trouve ici, mais c'est un
peu touffu :) :
<https://docs.oracle.com/javase/6/docs/technotes/guides/concurrency/

threadPrimitiveDeprecation.html>

Merci pour le lien. En fait ce que je veux faire est simple. J'ai une
carte qui comporte 4 relais branchée sur un raspberry. J'utilise une
bibliothèque écrite en C et une API écrite en Java qui utilise cette
bibliothèque. J'écris un petit programme qui mes les relais sur ON
pendant un certain nombre de secondes et sur OFF une autre durée. Comme
il y a 4 relais, j'ai créé 4 threads. J'arrive à allumer les relais.
J'arrive aussi à les éteindre. Mais par contre lorsque je quitte le
programme, les relais restent dans leur état. Quelquefois sur ON et
quelquefois sur OFF...
Il faut dire que je ne maitrise pas très bien les threads. Mais ce que je
veux faire n'est pas très compliqué. Je pense que je vais m'en sortir.
Il faudra bien faire attention quand même :
- Ne pas détruire le fil brutalement (avec Thread.stop(), qui de
toutes façons est obsolète).
- Utiliser un try/finally, pour être sûr que la finalisation soit
réalisée.

Je ne connais pas l'utilisation de try/finally mais je vais me documenter
sur ce sujet. Merci de m'avoir montré la direction dans laquelle chercher.
Suivant ce que tu veux dire par "n'existe pas" :
- Si tu n'as pas initialisé la variable (elle vaut null) : non, tu
vas avoir une NullPointException.
- Si le fil n'a simplement pas été lancé (via start()) : oui,
d'après la doc
(<https://docs.oracle.com/javase/6/docs/api/java/lang/

Thread.html#isAlive()>)
(le fil est en vie s'il a été démarré mais n'est pas encore mort)

Si je fais un interrupt(), dans quel état se trouvera mon thread? Est-ce
qu'il sera encore actif? Ou est-ce qu'il sera égal à null?
Je reposte le code de mon thread:
-----début du code-----
import com.pi4j.wiringpi.*;
/**
*
* @author jp
*/
public class ShroomThread extends Thread {// throws InterruptedException {
long ms_on, ms_off;
int pinmode;
//boolean isRunning = true;
ShroomThread( long on, long off, int pin ) {
this.ms_on = on;
this.ms_off = off;
this.pinmode = pin;
Gpio.pinMode( pinmode, Gpio.OUTPUT );
}
public void setGpioLow() {
Gpio.digitalWrite( pinmode, Gpio.LOW );
}
/**
*
*/
@Override
public void run() {
try {
while ( true ) {
if ( interrupted() ) {
throw new InterruptedException();
}
Gpio.digitalWrite( pinmode, Gpio.HIGH );
Gpio.delay( ms_on );
Gpio.digitalWrite( pinmode, Gpio.LOW );
Gpio.delay( ms_off );
}
}
catch ( InterruptedException e ) {}
}
}
-----fin du code-----
Dans la classe principale, je fais:
if ( t1 != null ) { t1.setGpioLow(); t1.interrupt(); }
ou bien:
if ( t1.isAlive() ) { t1.setGpioLow(); t1.interrupt(); }
Qu'est-ce qu'il vaut mieux?
A+
Avatar
Yliur
Le 15 Sep 2018 06:41:27 GMT
jp a écrit :
Le Fri, 14 Sep 2018 05:46:30 +0200, Yliur a écrit :
Là l'élément important c'est est-ce que la boucle est très longue
parce qu'elle réalise un traitement actif (des instructions sont
exécutées en permanence) ou bien elle est longue parce que le fil
est bloqué à différents moment ?

Il y a deux delay à l'intérieur d la boucle infinie qui peuvent durer
plusieurs minutes chacun.
Dans le deuxième cas, quelles sont les opérations bloquantes ? Il y
a des chances pour qu'elles répondent aux interruptions des fils
d'exécution via la mécanique de java (interrupt()).

Je ne sais pas... Je vais essayer de remplacer les delay, qui sont
des méthodes implémentées par l'API spécifique que j'utilise, par des
Thread.sleep(n); pour voir...
Suivant ton cas, j'essaierai d'expliquer plus en détail ce que tu
peux faire. La doc plus complète sur le sujet se trouve ici, mais
c'est un peu touffu :) :
<https://docs.oracle.com/javase/6/docs/technotes/guides/concurrency/
threadPrimitiveDeprecation.html>

Merci pour le lien. En fait ce que je veux faire est simple. J'ai une
carte qui comporte 4 relais branchée sur un raspberry. J'utilise une
bibliothèque écrite en C et une API écrite en Java qui utilise cette
bibliothèque. J'écris un petit programme qui mes les relais sur ON
pendant un certain nombre de secondes et sur OFF une autre durée.
Comme il y a 4 relais, j'ai créé 4 threads. J'arrive à allumer les
relais. J'arrive aussi à les éteindre. Mais par contre lorsque je
quitte le programme, les relais restent dans leur état. Quelquefois
sur ON et quelquefois sur OFF...
Il faut dire que je ne maitrise pas très bien les threads. Mais ce
que je veux faire n'est pas très compliqué. Je pense que je vais m'en
sortir.
Il faudra bien faire attention quand même :
- Ne pas détruire le fil brutalement (avec Thread.stop(), qui de
toutes façons est obsolète).
- Utiliser un try/finally, pour être sûr que la finalisation
soit réalisée.

Je ne connais pas l'utilisation de try/finally mais je vais me
documenter sur ce sujet. Merci de m'avoir montré la direction dans
laquelle chercher.
Suivant ce que tu veux dire par "n'existe pas" :
- Si tu n'as pas initialisé la variable (elle vaut null) : non,
tu vas avoir une NullPointException.
- Si le fil n'a simplement pas été lancé (via start()) : oui,
d'après la doc
(<https://docs.oracle.com/javase/6/docs/api/java/lang/
Thread.html#isAlive()>)
(le fil est en vie s'il a été démarré mais n'est pas encore
mort)

Si je fais un interrupt(), dans quel état se trouvera mon thread?
Est-ce qu'il sera encore actif? Ou est-ce qu'il sera égal à null?
Je reposte le code de mon thread:
-----début du code-----
import com.pi4j.wiringpi.*;
/**
*
* @author jp
*/
public class ShroomThread extends Thread {// throws
InterruptedException {
long ms_on, ms_off;
int pinmode;
//boolean isRunning = true;
ShroomThread( long on, long off, int pin ) {
this.ms_on = on;
this.ms_off = off;
this.pinmode = pin;
Gpio.pinMode( pinmode, Gpio.OUTPUT );
}
public void setGpioLow() {
Gpio.digitalWrite( pinmode, Gpio.LOW );
}
/**
*
*/
@Override
public void run() {
try {
while ( true ) {
if ( interrupted() ) {
throw new InterruptedException();
}
Gpio.digitalWrite( pinmode, Gpio.HIGH );
Gpio.delay( ms_on );
Gpio.digitalWrite( pinmode, Gpio.LOW );
Gpio.delay( ms_off );
}
}
catch ( InterruptedException e ) {}
}
}
-----fin du code-----
Dans la classe principale, je fais:
if ( t1 != null ) { t1.setGpioLow(); t1.interrupt(); }
ou bien:
if ( t1.isAlive() ) { t1.setGpioLow(); t1.interrupt(); }
Qu'est-ce qu'il vaut mieux?
A+

D'accord, je vois un peu mieux ce que tu veux faire.
Effectivement, je ne sais pas si Gpio.delay va réagir à interrupt : il
faudrait voir la doc ou bien faire un essai (mais la doc c'est mieux).
A priori InterruptedException est une "exception contrôlée" : tu aurais
une erreur de compilation dans ton code d'origine (celui sans "catch
( InterruptedException e )") si cette fonctions était capable de réagir
aux interruptions et qu'elle levait des InterruptedException. Sauf si
je manque quelque chose, elle ne sait pas le faire.
Donc tu as effectivement des traitements courts, entrecoupés de pauses
plus ou moins longues. Dans ce cas le code que tu as écrit ici
convient : tu vérifies si le fil est interrompu et tu quittes la boucle
(une exception pourquoi pas : ça permet de traiter tous les cas
d'interruption de la même manière). Si tu utilises Thread.sleep(),
l'attente sera effectivement interrompue par une InterruptedException
en cas de demande d'interruption du fil.
Dans la fonction run, tu peux écrire ça :
try
{
<le code actuel>
}
finally
{
<mettre le relais dans l'état final souhaité>
}
De cette manière, quand le code dans le try se terminera (normalement,
par une exception, même par un return, peu importe), ce qui se trouve
dans finally sera immédiatement exécuté. C'est donc là que tu veux
mettre le code permettant de remettre ton relais dans l'état final
souhaité. Attention si certaines opérations de finalisation sont
elles-mêmes susceptibles de lever des exceptions : il faut décider que
faire si ce cas se produit (par défaut le code du finally
s'interrompra, comme tout autre code qui rencontre une erreur).
Note que si tu fermes violemment ton programme depuis l'extérieur
tout ça ne sera sans doute pas exécuté : mieux vaut un moyen de lui
indiquer gentiment de se terminer et qu'il interrompe tous ses
traitement en cours. Si tu tues la JVM depuis l'extérieur, les
traitements de finalisation replaçant les relais dans le bon état ne
pourront pas s'exécuter. Pour plus de précisions, il faudrait savoir
comment tu utilises ton programmes / ce que tu veux en faire.
Depuis l'extérieur :
- À moins que tu en aies vraiment besoin, tu devrais éviter d'avoir
des variables t1 == null, ça simplifierait tes problèmes. Mais ça
dépend de ce que tu veux faire avec des fils d'exécution.
- Je ne sais pas à quoi sert t1.setGpioLow(); : s'il s'agit du code
permettant de replacer le relais dans son état final, voir
les remarques ci-dessus.
- A priori le fil sera considéré comme mort une fois interrompu
(parce qu'il aura terminé l'exécution de sa méthode run).
Et donc t1.isAlive() reverra faux. D'après la doc, tu ne peux pas
relancer un fil qui s'est terminé (qui est mort), mais tu peux
toujours en recréer un via new ShroomThread (...).
- Lorsque le fil sera interrompu, la variable qui contient une
référence sur l'objet ShroomThread ne passera pas toute seule à
null. Donc t1 == null sera toujours faux.
Pour plus de précisions sur la gestion des fils depuis l'extérieur, il
faudrait savoir quel est le but exact de la manœuvre (est-ce que tu
veux parfois les relancer, dans quelles conditions, ... ?).
À propos du reste de ton code :
- Les classes ne déclarent pas d'exceptions, la ligne
throws InterruptedException (en commentaire) ne peut pas servir à
cet endroit.
- En général on définit les attributs des classes comme privés
(private int ...), ce qui évite d'y accéder malencontreusement
depuis l'extérieur.
Avatar
jp
Le Sat, 15 Sep 2018 14:24:21 +0200, Yliur a écrit :
Depuis l'extérieur :
- À moins que tu en aies vraiment besoin, tu devrais éviter d'avoir
des variables t1 == null, ça simplifierait tes problèmes. Mais ça
dépend de ce que tu veux faire avec des fils d'exécution.
- Je ne sais pas à quoi sert t1.setGpioLow(); : s'il s'agit du code
permettant de replacer le relais dans son état final, voir les
remarques ci-dessus.

C'est ça.
- A priori le fil sera considéré comme mort une fois interrompu
(parce qu'il aura terminé l'exécution de sa méthode run).
Et donc t1.isAlive() reverra faux. D'après la doc, tu ne peux pas
relancer un fil qui s'est terminé (qui est mort), mais tu peux
toujours en recréer un via new ShroomThread (...).
- Lorsque le fil sera interrompu, la variable qui contient une
référence sur l'objet ShroomThread ne passera pas toute seule à
null. Donc t1 == null sera toujours faux.

Ok. Je vais faire les modifs en fonction de cette info.
Pour plus de précisions sur la gestion des fils depuis l'extérieur, il
faudrait savoir quel est le but exact de la manœuvre (est-ce que tu veux
parfois les relancer, dans quelles conditions, ... ?).

Oui, dans une JFrame j'ai un bouton 'start' et un bouton stop'.
À propos du reste de ton code :
- Les classes ne déclarent pas d'exceptions, la ligne
throws InterruptedException (en commentaire) ne peut pas servir à
cet endroit.

Ok. Je l'avais laissé en commentaires mais je l'enlève. De toutes façon,
si on le laisse ça ne fonctionne pas...
- En général on définit les attributs des classes comme privés
(private int ...), ce qui évite d'y accéder malencontreusement
depuis l'extérieur.

Ok. C'est changé. Maintenant ça fonctionne. J'ai juste un bug d'un cycle
qui reste actif quand je lance un relais qui est déjà allumé. Ça ne
s'arrête plus... Mais je pense pouvoir résoudre ce problème. C'est juste
une erreur de logique.
Merci pour tes conseils.
A+