[Ex] Script réentrant

Partagez vos scripts, et vos applications AutoIt.
Règles du forum
.
Répondre
Avatar du membre
sksbir
Niveau 7
Niveau 7
Messages : 384
Enregistré le : lun. 26 oct. 2009 17:57
Localisation : Lyon
Status : Hors ligne

[Ex] Script réentrant

#1

Message par sksbir »

Bonjour
voici un petit tutoriel pour aborder une notion sympathique en programmation : le script réentrant.

ça sert partout où
- il faut exécuter des choses en parallèles : on fait un script qui fait plusieurs run de lui-même.
- il faut exécuter des choses compliquées avec runas ou runaswait : on fait un script qui s'appelle lui même avec runaswait. C'est illustré ici [EX] Exécuter un script en tant qu'administrateur mais sans nommer le concept, ce qui est dommage quand on fait une recherche...

Le script réentrant permet de ne pas avoir à gérer 2(ou plus) codes sources ( un pour le père, un pour chaque fils ), mais un seul code source, ce qui permet de le maintenir cohérent plus facilement.

Exemples où ça peut servir : [..] Sons simultanés dans autoit ?
[..] Problème avec RunAS ... débutant


LA REENTRANCE , C'EST QUOI ?

Imaginons qu'on veut un programme qui affiche 2 msgbox, mais en même temps. On va créer un 1er script qui se contente d'afficher le contenu de CMDLINE[1] dans une msgbox:
- Appelons ce script mamsgbox.au3

Code : Tout sélectionner

 msgbox(16,"COUCOU",$CMDLINE[1])
 
On compile ce script pour avoir un EXE, et puis maintenant, on va créer un script qui va appeler 2 fois mamsgbox.exe , mais en même temps:
- Appelons ce script principal.au3

Code : Tout sélectionner

run("mamsgbox.exe 1er_message")
run("mamsgbox.exe 2me_message")
MsgBox(0,"FINNNN","fin du père")
 
Le programme lancé par l'utilisateur (ici donc : principal.exe) s'appellera ici "le programme père". Les programmes lancés par le programme père s'appellent "programmes fils".

On a donc bien maintenant un programme qui joue deux msgbox en même temps... Sauf que le programme père ne sait pas quand les 2 programmes "mamsgbox" se terminent : le script principal arrive tout de suite vers sa propre fin et basta.. c'est gênant si on veut coder quelque chose de plus compliqué, par exemple avec un traitement à ne continuer que lorsque tous les traitements fils se sont terminés.

Voici donc un programme père plus élaboré, qui attend la fin des fils : c'est facile, il suffit de récupérer le N° de process de chaque fils au moment du RUN, et d'attendre que ces processus aient disparus pour continuer le déroulement du père.

- script pere avec attente des fils:

Code : Tout sélectionner

$PID1=run("mamsgbox.exe 1er_message")
$PID2=run("mamsgbox.exe 2me_message")
; boucle d'attente de la fin des fils.
$FILS_ACTIFS=0
Do
    $FILS_ACTIFS=0
    If ProcessExists($PID1) Then $FILS_ACTIFS=1
    If ProcessExists($PID2) Then $FILS_ACTIFS=1
    ; pour pas faire un 100% CPU avec la boucle...
    If $FILS_ACTIFS Then sleep(100)
Until Not($FILS_ACTIFS)

MsgBox(0,"FINNNN","fin du père")
 
On a donc maintenant un script père qui attend qu'on ait répondu au 2 msgbox "x message" avant de lancer sa propre msgbox de fin.

Maintenant, on va aborder le coeur même du principe de la réentrance : Plutôt que d'écrire deux scripts différents, pourquoi ne pas écrire qu'un seul script ?
Si le script est appelé sans argument, c'est que c'est l'utilisateur qui l'a appelé. Si le script est appelé avec un argument, alors c'est que c'est le script père qui l'a appelé . Autrement dit, on va avoir un script séparé en 2 parties : père et fils.
En fait, le script s'appelle lui même avec run, mais comme on a prévu un traitement différent suivant la présence ou non d'un argument, le script se comporte à la fois comme le père et le fils ( ça va, vous suivez ? :D )

Code : Tout sélectionner

; La partie "FILS" du script
If $CMDLINE[0] = 1 Then
;
; ICI, ce qu'on fait en tant que FILS
;
else
;
; ICI, ce qu'on fait en tant que PERE
;
endif
 
Voici une version plus concrète:
► Afficher le texte


Voila, on touche au but. maintenant, si on veut pouvoir gérer un nombre de scripts fils plus important, il faut passer aux tableaux ( imaginez la gueule du script au dessus avec 10 msgbox à lancer en même temps... ).
ça permet aussi de faire une boucle pour lancer tous les fils, puis une 2eme boucle qui va scanner la présence de tous les fils lancé
► Afficher le texte

Je traite la partie "fils" avant la partie "père" : A l'usage, c'est mieux de faire comme ça que l'inverse : le code du fils est généralement assez court, et il vaut mieux le mettre devant.


Il reste un problème potentiel : Si on en a besoin, comment les fils vont remonter des informations à la partie "père" du script ?
La seule solution consiste à passer par un fichier.
Chaque fils connait son propre PID ( @AutoitPID ). Si le fils écrit des résultats dans un fichier du genre @scriptname & "_" & @AutoitPID , le père saura retrouver ce fichier dès que le fils aura terminé son exécution...
Modifié en dernier par sksbir le sam. 01 févr. 2014 10:22, modifié 1 fois.
Avatar du membre
jl56
Niveau 7
Niveau 7
Messages : 411
Enregistré le : mer. 24 oct. 2007 22:42
Localisation : 56000
Status : Hors ligne

Re: [Ex] Script réentrant

#2

Message par jl56 »

Bonsoir

Bien que je vois pas dans l'immédiat d’application concrète votre post est très intéressant et bien expliqué

Merci pour ce partage

A+

JL56
Avatar du membre
ZDS
Membre émérite
Membre émérite
Messages : 554
Enregistré le : jeu. 10 juin 2010 10:35
Localisation : 22300 Cul-d'chouette Langue-de-vache
Status : Hors ligne

Re: [Ex] Script réentrant

#3

Message par ZDS »

Bonjour,

C'est sans doute très utile en algorithmique distribuée (plusieurs petits processus qui font le boulot d'un gros), et pour les exemples pompeux et tarabiscottés (cf sémaphores, je sais me foutre de ma propre tronche ^^), mais malheureusement AutoIt ne s'y prête pas, sauf dans un cas qui saute aux yeux, mais aux performances limitées : la boite de dialogue MsgBox qui est normalement bloquante.

Mais c'est tout ce que je vois de réellement utilisable. En effet, faire une réentrance dans un script demande X ressources supplémentaires que ce soit en terme de temps de CPU (même si c'est sur un autre coeur, et même ça c'est pas sûr, Tlem ou Jchd en sauront plus que moi) et en terme de RAM. Ne gardant que l'exemple de la RAM, pour un script qui en bouffe 10Mo (qui est très peu), faire une distribution sur 10 réentrances en consommeront 100 Mo (ce qui devient quand même conséquent), même si le process père est en attente. Mieux vaut déléguer le travail à des scripts plus petits et optimisés à coté, qui pourront eux être appelés au besoin (et asynchrones, tant qu'à faire).

Il y a aussi une faille dans ton système, celui de la communication des infos entre les différents processus; mais pour cela, je te conseille d'aller faire un tour sur le topic dont je t'ai parlé plus haut sur les sémaphores (lien).

A bientôt, et bonne chance !

PS1: Cela n’empêche que l'explication est claire et intéressante, rien à dire ce coté :) Bravo.

PS2: quand je parlais d'exemples pompeux, je voulais dire par là que c'est bien joli de faire de la récursivité (terminale ou pas) quand une simple boucle for ou while suffit. et comme j'aime bien illustrer mes conneries avec d'autres conneries ^^ :
J'ai un problème. Je me dis : Tiens, je vais faire du multi-process réentrant. Maintenant j'ai un + un + un + un + un + un + un + un + un + un ... problèmes.
ZDS : Chef de projet du nAiO (logiciel AutoIt gratuit sous licence CC 4.0 BY-NC-SA)
Tout problème a une solution, donc si il y a pas d'solution, c'est qu'il y a pas d'problème !
Avatar du membre
sksbir
Niveau 7
Niveau 7
Messages : 384
Enregistré le : lun. 26 oct. 2009 17:57
Localisation : Lyon
Status : Hors ligne

Re: [Ex] Script réentrant

#4

Message par sksbir »

ZDS a écrit :Bonjour,
C'est sans doute très utile en algorithmique distribuée (plusieurs petits processus qui font le boulot d'un gros), et pour les exemples pompeux et tarabiscottés (cf sémaphores, je sais me foutre de ma propre tronche ^^), mais malheureusement AutoIt ne s'y prête pas, sauf dans un cas qui saute aux yeux, mais aux performances limitées : la boite de dialogue MsgBox qui est normalement bloquante.
Mais c'est tout ce que je vois de réellement utilisable. En effet, faire une réentrance dans un script demande X ressources supplémentaires que ce soit en terme de temps de CPU....
Bonjour
Même si l'exemple de codage que j'ai proposé apporte peut-etre la confusion entre "réentrance" et "parallélisation" , la parallélisation massive de processus n'est qu'un des aspects de la réentrance.
Voici au moins deux exemples concrets où j'ai personnellement utilisé la réentrance, et ce, à titre professionnel :
- un ( et un seul ) script qui fait un runas de lui même : j'ai écris le script pour permettre à des utilisateurs non admin du domaine d'avoir des actions ciblées et loggées : changer le mot de passe des utilisateurs. La partie père vérifie les autorisations, gère l'interactivité avec l'utilisateur, logge les changements de mot de passe, la partie fils du script s'active avec un argument précis envoyé par le père via runas et s'exécute sans interaction avec l'utilisateur.
Le seul intérêt ici, c'est comme je l'ai dit dans le 1er topic : ne gérer qu'un seul code source, et obtenir une application autonome ( un seul binaire et basta. ) ( et oui, je suis au courant pour la faille de sécurité que ça représente vis à vis de la présence du mot de passe dans l'exe, le script s'adressant à une population de type "standardiste", le risque a été calculé )

- un script chargé d'interroger une grosse liste de serveurs.L'interrogation des serveurs séquentiellement aurait pris trop de temps,surtout quand un certain nombre de serveurs sont arrêtés. Le père fragmente la liste et délègue à un certain nombre de fils ( 4-5 )

Quand à la remontée d'information par les fils, je ne connaissais pas les sémaphores, mais j'utilise une méthode sans doute "préhistorique" (genre massue et silex :lol: ), mais qui a l'avantage d'être éprouvée :
- chaque fils génère ses résultats dans @scriptname_FILS_@AutoitPID.log
- quand le père détecte la fin de tous les fils, il analyse tout ce qui traine dans @scriptname_FILS_*.log . Et si besoin, le père sait aussi à quel fils appartient quel fichier puisqu'il a conservé un tableau des PID des fils...
C'est pas difficile à coder et c'est pourquoi je n'ai fait qu'évoquer cette méthode dans le 1er topic.
Répondre