PC SOFT

FOROS PROFESIONALES
WINDEVWEBDEV y WINDEV Mobile

Inicio → WINDEV 23 → Trace et threads : Blocage
Trace et threads : Blocage
Iniciado por dev_compil, 24,sep. 2014 17:51 - 7 respuestas
Miembro registrado
26 mensajes
Publicado el 24,septiembre 2014 - 17:51
Bonjour,

Je souhaite avoir un process qui se déroule en plusieurs étapes. Les étapes se succèdent les unes aux autres, chacune dans son thread secondaire. Pour ce faire j'essaie d'adopter une architecture simple basée sur 3 threads :

1. Thread d'attente du signal de lancement : THR_WAIT
Ce thread est lancé dès l'ouverture de la fenêtre.
Il attend la réception d'un signal pour lancer le thread secondaire d'exécution puis un autre thread secondaire qui attend la fin du thread d’exécution

2. Thread d'exécution de la fonction liée à l'étape : THR_RUN
Durant l'exécution la variable etape_suivante est fixée.

3. Thread de surveillance de la fin d’exécution du thread d'exécution : THR_END
Ce thread renvoi un signal au thread d'attente pour lancer l'étape suivante et se termine

Tout se déroule pour le mieux jusqu'à ce que l'on doit utiliser la fonction trace. La doc indique que la fonction trace est similaire à la fonction info. Je crée une fonction 'thrp_trace' qui exécutera la fonction trace depuis le thread principal.

Tout va pour le mieux, c-à-d que les étapes se succèdent normalement jusqu'au moment où ... on agit sur la fenêtre de trace (déplacement, redimensionnement, ...) lors du débogage.
Symptôme : Le thread d'éxécution semble en pause ! Les windgets (libellés, ...) et trace traduisent la fin d'activité de l'étape.
Mieux : Le thread de surveillance ne détecte même pas la mort du thread d'exécution pour renvoyer un signal au thread d'attente de signal !
J'ai créé une fenêtre soeur avec un bouton pour afficher l'état courant des 3 threads. Le thread d'exécution est annoncé toujours en cours pourtant aucun signe de vie !






Un peu de code :-)

Initialisation du projet
ThreadMode(threadSectionCritique)


Initialisation de la fenêtre
OuvreSoeur(FEN_Threads) // Témoin des états des threads

ThreadExecute("P1_THR_WAIT",threadNormal+threadAttendDémarrage,COL_Process.thr_waitP1)
// Thread de surveillance/lancement des étapes

COL_Process.geEtapeSuivante = EP1Etapes.RETOUR_IDDLE// Initialisation
ThreadEnvoieSignal("P1_THR_WAIT")



Bouton de lancement
COL_Process.geEtapeSuivante = EP1Etapes.ETAPE_01
ThreadEnvoieSignal("P1_THR_WAIT")



Procedure thr_waitP1()

BOUCLE

ThreadAttendSignal(Infini)

SI PAS ThreadEtat("P1_THR_END") = threadInexistant ALORS

ThreadAttend("P1_THR_END",Infini)

FIN

COL_Process.thrp_next()
//ExécuteThreadPrincipal(COL_Process.thrp_next)

FIN


Procedure thrp_next()


SI COL_Process.geEtapeSuivante = EP1Etapes.IDDLE ALORSRETOUR// Ne rien faire !

SI PAS ThreadEtat("P1_THR_RUN") = threadInexistant ALORS// Thread déjà en cours !!!!
RETOUR
FIN


SELON COL_Process.geEtapeSuivante

CAS EP1Etapes.ETAPE_01 :
COL_Process.geEtapeEnCours = EP1Etapes.ETAPE_02
COL_Process.geEtapeSuivante = EP1Etapes.RETOUR_IDDLE
ThreadExecute("P1_THR_RUN",threadNormal+threadAttendDémarrage,COL_Process.etape1)

CAS EP1Etapes.ETAPE_02 :
COL_Process.geEtapeEnCours = EP1Etapes.ETAPE_02
COL_Process.geEtapeSuivante = EP1Etapes.RETOUR_IDDLE
ThreadExecute("P1_THR_RUN",threadNormal+threadAttendDémarrage,COL_Process.etape2)

CAS EP1Etapes.ETAPE_03 :
COL_Process.geEtapeEnCours = EP1Etapes.ETAPE_03
COL_Process.geEtapeSuivante = EP1Etapes.RETOUR_IDDLE
ThreadExecute("P1_THR_RUN",threadNormal+threadAttendDémarrage,COL_Process.etape3)

CAS EP1Etapes.ETAPE_04 :
COL_Process.geEtapeEnCours = EP1Etapes.ETAPE_04
COL_Process.geEtapeSuivante = EP1Etapes.RETOUR_IDDLE
ThreadExecute("P1_THR_RUN",threadNormal+threadAttendDémarrage,COL_Process.etape4)

CAS EP1Etapes.ETAPE_05 :
COL_Process.geEtapeEnCours = EP1Etapes.ETAPE_05
COL_Process.geEtapeSuivante = EP1Etapes.RETOUR_IDDLE
ThreadExecute("P1_THR_RUN",threadNormal+threadAttendDémarrage,COL_Process.etape5)

CAS EP1Etapes.RETOUR_IDDLE :
COL_Process.geEtapeEnCours = EP1Etapes.RETOUR_IDDLE
COL_Process.geEtapeSuivante = EP1Etapes.IDDLE
ThreadExecute("P1_THR_RUN",threadNormal+threadAttendDémarrage,COL_Process.etape_retourIddle)

AUTRE CAS

FIN

// Thread de surveillance de fin du thread d'exécution
ThreadExecute("P1_THR_END",threadNormal+threadAttendDémarrage,COL_Process.thr_endP1)


Procedure PRIVÉE thr_endP1()

ThreadAttend("P1_THR_RUN", Infini)

ThreadEnvoieSignal("P1_THR_WAIT")



Exemple de code d'une étape
Procedure PRIVÉE etape2()

ExécuteThreadPrincipal(thrp_etape2) // Afficher des infos (libellé, ...) liée à l'étape


thrp_trace("Fin étape 2")

COL_Process.geEtapeSuivante = EP1Etapes.ETAPE_03

ThreadPause(100)



Procedure thrp_trace(sMsg est une chaîne)
//

ExécuteThreadPrincipal(_trace,sMsg)



Procedure PRIVÉE _trace(sMsg est une chaîne)
// Fonction d'appel de la fonction trace (depuis le thread principal)

// Des essais on été fait sans section critique ni tracedébut
SectionCritiqueDébut()

TraceDébut(trFenêtre)
Trace(sMsg)

SectionCritiqueFin()



Procedure PRIVÉE thrp_etape2()
// Exemple de fonction d'affichage appelée durant l'étape 2

SectionCritiqueDébut()

FEN_principale.LIB_Proc_etape = "Etape 2"

SectionCritiqueFin()



Merci pour votre aide
Publicado el 24,septiembre 2014 - 19:10
Bonjour

Les threads servent à UNE chose : faire plusieurs traitements en MEME TEMPS.

La, tu les utilise pour faire un traitement APRES l'autre, ce qui se
fait sans aucune complication avec trois procédures tout à fait normales.

Donc, au départ, la logique de ton code est complètement fausse.

Ensuite, une des limitations des threads est qu'il ne faut JAMAIS
accéder à l'interface utilisateur. Trace, Info, accéder aux champs, etc,
tout ca est interdit.

Donc, encore une fois, mauvaise pioche...

Je ne sais pas pourquoi tu fais ce que tu fais, mais tu es parti
complètement dans la mauvaise direction, d'après ce que tu nous décrit.

Cordialement


--
Fabrice Harari
Consultant WinDev, WebDev et WinDev Mobile International

NOUVEAU: WXReplication, votre système de réplication open source est
disponible sur mon site web !!!
WXShowroom.com : Montrez vos projets !
Plus d'information sur http://fabriceharari.com

On 9/24/2014 9:51 AM, dev_compil wrote:
Bonjour,
Je souhaite avoir un process qui se déroule en plusieurs étapes. Les
étapes se succèdent les unes aux autres, chacune dans son thread
secondaire. Pour ce faire j'essaie d'adopter une architecture simple
basée sur 3 threads :

1. Thread d'attente du signal de lancement : THR_WAIT
Ce thread est lancé dès l'ouverture de la fenêtre. Il attend la
réception d'un signal pour lancer le thread secondaire d'exécution puis
un autre thread secondaire qui attend la fin du thread d’exécution

2. Thread d'exécution de la fonction liée à l'étape : THR_RUN
Durant l'exécution la variable etape_suivante est fixée.

3. Thread de surveillance de la fin d’exécution du thread d'exécution :
THR_END
Ce thread renvoi un signal au thread d'attente pour lancer l'étape
suivante et se termine

Tout se déroule pour le mieux jusqu'à ce que l'on doit utiliser la
fonction trace. La doc indique que la fonction trace est similaire à la
fonction info. Je crée une fonction 'thrp_trace' qui exécutera la
fonction trace depuis le thread principal.
Tout va pour le mieux, c-à-d que les étapes se succèdent normalement
jusqu'au moment où ... on agit sur la fenêtre de trace (déplacement,
redimensionnement, ...) lors du débogage.
Symptôme : Le thread d'éxécution semble en pause ! Les windgets
(libellés, ...) et trace traduisent la fin d'activité de l'étape.
Mieux : Le thread de surveillance ne détecte même pas la mort du thread
d'exécution pour renvoyer un signal au thread d'attente de signal ! J'ai
créé une fenêtre soeur avec un bouton pour afficher l'état courant des 3
threads. Le thread d'exécution est annoncé toujours en cours pourtant
aucun signe de vie !







Un peu de code :-)

Initialisation du projet
ThreadMode(threadSectionCritique)


Initialisation de la fenêtre
OuvreSoeur(FEN_Threads) // Témoin des états des threads

ThreadExécute("P1_THR_WAIT",threadNormal+threadAttendDémarrage,COL_Process.thr_waitP1)

// Thread de surveillance/lancement des étapes

COL_Process.geEtapeSuivante = EP1Etapes.RETOUR_IDDLE //
Initialisation
ThreadEnvoieSignal("P1_THR_WAIT")



Bouton de lancement
COL_Process.geEtapeSuivante = EP1Etapes.ETAPE_01
ThreadEnvoieSignal("P1_THR_WAIT")



PROCEDURE thr_waitP1()

BOUCLE
ThreadAttendSignal(Infini)

SI PAS ThreadEtat("P1_THR_END") = threadInexistant ALORS

ThreadAttend("P1_THR_END",Infini)

FIN

COL_Process.thrp_next()
// ExécuteThreadPrincipal(COL_Process.thrp_next)

FIN


PROCEDURE thrp_next()


SI COL_Process.geEtapeSuivante = EP1Etapes.IDDLE ALORS
RETOUR // Ne rien faire !

SI PAS ThreadEtat("P1_THR_RUN") = threadInexistant ALORS
// Thread déjà en cours !!!!
RETOUR
FIN


SELON COL_Process.geEtapeSuivante

CAS EP1Etapes.ETAPE_01 :
COL_Process.geEtapeEnCours = EP1Etapes.ETAPE_02
COL_Process.geEtapeSuivante = EP1Etapes.RETOUR_IDDLE

ThreadExécute("P1_THR_RUN",threadNormal+threadAttendDémarrage,COL_Process.etape1)


CAS EP1Etapes.ETAPE_02 :
COL_Process.geEtapeEnCours = EP1Etapes.ETAPE_02
COL_Process.geEtapeSuivante = EP1Etapes.RETOUR_IDDLE

ThreadExécute("P1_THR_RUN",threadNormal+threadAttendDémarrage,COL_Process.etape2)


CAS EP1Etapes.ETAPE_03 :
COL_Process.geEtapeEnCours = EP1Etapes.ETAPE_03
COL_Process.geEtapeSuivante = EP1Etapes.RETOUR_IDDLE

ThreadExécute("P1_THR_RUN",threadNormal+threadAttendDémarrage,COL_Process.etape3)


CAS EP1Etapes.ETAPE_04 :
COL_Process.geEtapeEnCours = EP1Etapes.ETAPE_04
COL_Process.geEtapeSuivante = EP1Etapes.RETOUR_IDDLE

ThreadExécute("P1_THR_RUN",threadNormal+threadAttendDémarrage,COL_Process.etape4)


CAS EP1Etapes.ETAPE_05 :
COL_Process.geEtapeEnCours = EP1Etapes.ETAPE_05
COL_Process.geEtapeSuivante = EP1Etapes.RETOUR_IDDLE

ThreadExécute("P1_THR_RUN",threadNormal+threadAttendDémarrage,COL_Process.etape5)


CAS EP1Etapes.RETOUR_IDDLE :
COL_Process.geEtapeEnCours = EP1Etapes.RETOUR_IDDLE
COL_Process.geEtapeSuivante = EP1Etapes.IDDLE

ThreadExécute("P1_THR_RUN",threadNormal+threadAttendDémarrage,COL_Process.etape_retourIddle)


AUTRE CAS

FIN

// Thread de surveillance de fin du thread d'exécution
ThreadExécute("P1_THR_END",threadNormal+threadAttendDémarrage,COL_Process.thr_endP1)


PROCEDURE PRIVÉE thr_endP1()

ThreadAttend("P1_THR_RUN", Infini)

ThreadEnvoieSignal("P1_THR_WAIT")



Exemple de code d'une étape
PROCEDURE PRIVÉE etape2()

ExécuteThreadPrincipal(thrp_etape2) // Afficher des
infos (libellé, ...) liée à l'étape


thrp_trace("Fin étape 2")

COL_Process.geEtapeSuivante = EP1Etapes.ETAPE_03

ThreadPause(100)



PROCEDURE thrp_trace(sMsg est une chaîne)
//
ExécuteThreadPrincipal(_trace,sMsg)



PROCEDURE PRIVÉE _trace(sMsg est une chaîne)
// Fonction d'appel de la fonction trace (depuis le thread principal)

// Des essais on été fait sans section critique ni tracedébut
SectionCritiqueDébut()

TraceDébut(trFenêtre)
Trace(sMsg)

SectionCritiqueFin()



PROCEDURE PRIVÉE thrp_etape2()
// Exemple de fonction d'affichage appelée durant l'étape 2

SectionCritiqueDébut()

FEN_principale.LIB_Proc_etape = "Etape 2"

SectionCritiqueFin()



Merci pour votre aide
Miembro registrado
40 mensajes
Popularité : +1 (1 vote)
Publicado el 25,septiembre 2014 - 10:00
bonjour
cela dépend du contexte, des thread sont parfois nécessaires même lorsqu'il y a plusieurs étapes chronologiques.
Exemples : le pesage de camions, il faut gérer
1 - les feux bicolore d'entré de pont bascule,
2 - la prise du la tare du camion
3 - le gyrophare pour autoriser la sortie du pont

et pendant ce temps il faut générer des interruptions pour permettre au chauffeur de saisir des information (immatriculation etc..) sur une borne
et d'autres interruptions pour permettre à un opérateur de corriger le infos du chauffeur si besoin est.
j'ai une appli qui fonctionne depuis 4 ans sans problèmes.

pour simplifier j'ai 3 Thread un pour gérer les feux, un pour gérer l'indicateur de pesage et un pour gérer le dialogue borne-chauffeur
dialogue borne-chauffeur correspond à ton problème il y a plusieurs étapes dans le dialogue, mais un seul thread, le thread est une BOUCLE dans lequel il y a un SELON pour les différentes etapes.
global du projet
GLOBAL
gbBloquer est un booleen
....

BOUCLE
si bFin = vrai alors sortir
si gbBloquer = vrai alors
THREADPAUSE ....
CONTINUE
fin
SELON nEtape
CAS 1: ....
nEtape = 2
CAS 2: ....
nEtape = 3
CAS 3: ....
AUTRE CAS: ....
FIN

pour debugger j'écris dans un fichier texte différentes information, pas de trace ni d'info (on ne doit pas bloquer un thread en utilisant l'interface utilisateur) par contre le thread peut afficher des information sur l'écran dans des champs non accessibles à l'opérateur.
si l'opérateur doit saisir des infos dans la fenêtre, je bloque le thread (ex : l'operateur appuie sur F1 pour démarrer la saisie gbBloquer = vrai le thread se bloque, l'operateur valide gbBloquer = FAUX le thread redemarre)

de la même manière le thread BORNE-CHAUFFEUR peut agir sur le thread des FEUX au moyen d'une global de la fenetre.
Publicado el 30,septiembre 2014 - 10:41
Bonjour Fabrice,


Merci pour ta réponse concernant ce problème de blocage. Effectivement, appeler des fonctions classique serait une meilleure architecture. J'ai fait d'autres tests. Le souci vient bien de la fonction trace qu'il ne faut pas exécuter en parallèle aux threads (la nouvelle architecture est exposée plus loin)
Publicado el 30,septiembre 2014 - 11:05
J'ai fait d'autres tests avec une architecture beaucoup plus simple.

Un simple thread affichant les nombres issus d'une boucle. Tout affichage passe par le thread principal (ExécuteThreadPrincipal)






Procedure mon_thread()

LOCAL
nImax est un entier = 1000

ExécuteThreadPrincipal(_thrp_init, nImax)

POUR i = 1 _A_ nImax

ExécuteThreadPrincipal(_thrp_gui,i)

thrp_trace("#"+i)

FIN


La fonction de MAJ des informations de l'interface

Procedure PRIVÉE _thrp_gui(nb est un entier)

LIB_thr1 = "#" + nb

JAUGE_thr1..Valeur = nb


Pour bien isoler la fonction trace, celle-ci est aussi appelée depuis le thread principal.

Procedure thrp_trace(msg est une chaîne)

ExécuteThreadPrincipal(_trace, msg)



Procedure PRIVÉE _trace(sMsg est une chaîne)

Trace(sMsg)


L'architecture est on ne peut plus simple.

Résultat : toute action sur la fenêtre de trace (déplacement, redimensionnement, ...) interrompt le thread (sans pour autant le
tuer !)

Avez-vous observé ce même comportement des threads en phase de débogage ?
Miembro registrado
4 mensajes
Publicado el 30,septiembre 2014 - 12:58
bonjour
Les threads sont super quand on les maitrise, j'ai eu du mal dans de multiples langages mais
Tu as toi même la réponse " qu'il ne faut pas exécuter en parallèle aux threads", j'appelais cela l'embrassade la mort...
Qui devient "prioritaire" dans ces threads car c'est du multiplexage?..à un moment un Thread devance l'autre et l'attente se fait indéfiniment car il y a un qui est : soit mort, soit en attente ou Thread toujours vivant solitairement.

ça me fait penser aux fonctions recursif? si cela vous fait tilt qui sont gourmands dans ce genre de boulot et abominable si on sais pas gerer les sorties.
voila, mon pavé philo pour qq instants..
bonne continuation..


dev_compil a écrit :
Bonjour Fabrice,


Merci pour ta réponse concernant ce problème de blocage. Effectivement, appeler des fonctions classique serait une meilleure architecture. J'ai fait d'autres tests. Le souci vient bien de la fonction trace qu'il ne faut pas exécuter en parallèle aux threads (la nouvelle architecture est exposée plus loin)
Publicado el 12,julio 2018 - 17:48
Bonjour,

Il n'y a pas eu d'activités dans cette article depuis un bout de temps, mais je voudrais ajouter de l'info.

Je croit très fortement que c'est un bug de Windev, je suis tombé sur le même problème. Au début je croyait que c'était une erreur dans mon code où un thread tombait dans un état de 'deadlock' mais apparament non.

Après plusieurs heures de gaspillées/debugging j'ai isolé le problème à l'appel de ExécuteThreadPrincipal. Suite à un clic/mouvement/re-dimensionnement de la fenêtre de Trace. De ce que je peut comprendre, le thread secondaire devient suspendu en état 'Running' car la fonction ExécuteThreadPrincipal est 'locking' tant que la fonction passé en paramètre n'est pas exécuté. Apparament, cliquer dans la fenêtre de trace affecte l'exécution sur le thread principal d'une façon que j'ignore.

Solution: Ne pas toucher la fenêtre de trace lorsque vous utiliser plus qu'un thread 8)

PS: Voici ce que le trace affiche avant & après avoir cliquer dans la fenêtre de trace.



Publicado el 13,julio 2018 - 08:54
bonjour
ce qui veut dire que les threads de debogage peuvent interferer dans l'execution de l'appli afin justement de le debogger
OUh lala...
bien bon a savoir!!!
:(