DOCUMENTATION EN LIGNE
DE WINDEVWEBDEV ET WINDEV MOBILE

Aide / WLangage / Fonctions WLangage / Fonctions standard / Fonctions de gestion des traitements / Tâches parallèles
  • Présentation
  • Principe
  • Comment le faire ?
  • Principe
  • Créer et exécuter une tâche parallèle
  • Le mot-clé MaTâcheParallèle
  • Attendre l'exécution de la tâche et récupérer la valeur de retour
  • Gérer l'enchaînement des tâches parallèles
  • Manipuler les champs depuis une tâche parallèle
  • Exemple : Accélérer les traitements
  • Accélérer les traitements : calcul de statistiques
  • Exemple : Améliorer la réactivité de l'application
  • Améliorer la réactivité de l'application
  • Mise à jour l'IHM via une tâche de continuation
  • Mise à jour l'IHM via une procédure exécutée dans le thread principal
WINDEV
WindowsLinuxUniversal Windows 10 AppJavaEtats et RequêtesCode Utilisateur (MCU)
WEBDEV
WindowsLinuxPHPWEBDEV - Code Navigateur
WINDEV Mobile
AndroidWidget AndroidiPhone/iPadWidget IOSApple WatchMac CatalystUniversal Windows 10 App
Autres
Procédures stockées
Présentation
A l'heure actuelle, la puissance des machines augmente. Les machines ont des processeurs puissants avec plusieurs coeurs.
Afin d'augmenter les performances des applications et de faire travailler un nombre maximum de coeurs de la machine, il est désormais possible de découper les traitements en une liste de sous-traitements (appelés tâches) et de les exécuter en parallèle plutôt qu'en séquentiel.
Une tâche est une procédure à exécuter qui peut attendre des paramètres en entrée et retourner un résultat. Ces tâches seront exécutées par la machine dans un ou plusieurs threads en fonction de la disponibilité de la machine.
Une tâche peut être elle-même décomposée en plusieurs sous-tâches.
Quel est l'intérêt des tâches parallèles ?
Les tâches parallèles sont utiles pour :
  • accélérer les temps de traitement de l'application grâce au parallélisme : plusieurs traitements sont exécutés en parallèle au lieu d'être exécutés séquentiellement : la vitesse de l'application est améliorée.
    Un exemple simple : lancer un calcul de statistiques d'envoi et de réception d'emails sur chaque adresse email de la base de données. Si le calcul de statistiques sur une adresse email prend une seconde et si la base de données contient 200 000 adresses email, le calcul prend plus de deux jours.
    Pour aller plus vite, il suffit de lancer une tâche parallèle pour chaque adresse email trouvée.
    Cet exemple est détaillé dans le paragraphe Exemple : Accélérer les traitements.
  • améliorer la réactivité de l'application : plusieurs traitements longs et bloquants sont effectués en parallèle au lieu d'être exécutés séquentiellement : l'utilisateur n'a pas l'impression d'être bloqué.
    Un exemple simple : Un champ Table affiche une liste de contacts dont la photo est chargée depuis une adresse Internet. Pour chaque contact, l'application fait une requête Internet (ce qui entraîne un ralentissement).
    Pour que le remplissage du champ Table ne soit pas bloqué, le traitement d'affichage de ligne lance une tâche parallèle permettant de lancer la requête Internet et de mettre à jour l'IHM si nécessaire.
    Cet exemple est détaillé dans le paragraphe Exemple : Améliorer la réactivité de l'application.
Principe
Pour gérer des tâches parallèles, le WLangage propose :
  • un type de variable TâcheParallèle. Ce type de variable permet de manipuler une tâche parallèle. Ce type de variable ne permet pas de modifier les caractéristiques d'une tâche parallèle.
  • des fonctions de gestion des tâches (TâcheParallèle*).
Remarque : Il est également possible d'utiliser le type de variable Description de TâcheParallèle. Ce type de variable permet uniquement de décrire une tâche parallèle. Lorsque la tâche parallèle est définie, il sera impossible de modifier ses caractéristiques.
Comment le faire ?

Principe

Pour mettre en place une gestion de tâches parallèles, il faut :
  1. Créer et exécuter une tâche parallèle.
  2. Attendre l'exécution de la tâche et récupérer la valeur de retour.
  3. Gérer si nécessaire l'enchaînement des tâches parallèles.
  4. Manipuler si nécessaire les champs depuis une tâche parallèle.

Créer et exécuter une tâche parallèle

Une tâche parallèle doit être associée à une variable de type TâcheParallèle.
Il est possible de déclarer une variable de type TâcheParallèle de plusieurs façons :
  • Déclaration simple. La description de la tâche parallèle est effectuée lors de son exécution avec la fonction TâcheParallèleExécute :
    // Déclare une variable pour manipuler une tâche parallèle
    t est une TâcheParallèle
    // Exécution et description de la tâche parallèle
    t = TâcheParallèleExécute(Proc, ("Premier paramètre", 2))
  • Déclaration et description de la tâche parallèle. La tâche parallèle est ensuite exécutée grâce à la fonction TâcheParallèleExécute.
    // Construit une tâche parallèle
    t est une TâcheParallèle(Proc, ("Premier paramètre", 2))
    // Déclenche l'exécution de la tâche parallèle
    TâcheParallèleExécute(t)
Remarque : Lors de la description de la tâche parallèle, il est possible d'indiquer :
  • la procédure à exécuter.
  • les paramètres attendus par la procédure.
  • le mode d'exécution de la tâche parallèle : gestion des contextes HFSQL et interactions avec le thread principal.

Le mot-clé MaTâcheParallèle

Le mot-clé MaTâcheParallèle permet de manipuler la tâche parallèle en cours et de connaître ses propriétés. Il est ainsi possible dans le code exécuté par une tâche parallèle d'accéder aux informations concernant la tâche parallèle en cours. Les propriétés accessibles sont celles d'une variable de type TâcheParallèle :
Nom de la propriétéType manipuléEffet
AnnuléeBooléen
  • Vrai si la tâche est annulée,
  • Faux dans le cas contraire.
Cette propriété est disponible en lecture seulement.
EtatConstante de type EntierEtat de la tâche :
  • tpeAnnulée : la tâche parallèle est annulée (fonction TâcheParallèleAnnule).
  • tpeAnnulationDemandée : une demande d'annulation a été faite sur la tâche parallèle (fonction TâcheParallèleDemandeAnnulation).
  • tpeAttenteExécution : la tâche parallèle est en attente d'exécution.
  • tpeAttentePrécédente : la tâche parallèle attend l'exécution d'une tâche parallèle précédente.
  • tpeExécutionEnCours : la tâche parallèle est en cours d'exécution.
  • tpeNonPlanifiée : la tâche parallèle n'est pas planifiée.
  • tpeTerminée : la tâche parallèle est terminée.
Cette propriété est disponible en lecture seulement.
IdentifiantEntierIdentifiant de tâche. Cet identifiant peut être utilisé par exemple à des fins de débogage.
Cette propriété est disponible en lecture seulement.
TerminéeBooléen
  • Vrai si la tâche est terminée,
  • Faux dans le cas contraire.
Cette propriété est disponible en lecture seulement.
ValeurRenvoyéeValeur renvoyée par la tâche. Attention :
  • Si la tâche est toujours en cours, la propriété ValeurRenvoyée attend la fin de la tâche.
  • Si la tâche est terminée sans erreur fatale, la propriété renvoie la ou les valeurs de retour de la procédure de la tâche.
Cette propriété est disponible en lecture seulement.

Attendre l'exécution de la tâche et récupérer la valeur de retour

Plusieurs tâches parallèles peuvent être lancées simultanément. Il est possible d'attendre l'exécution d'une ou de plusieurs tâches parallèles avant d'exécuter un traitement :
TâcheParallèleAttendAttend la fin de l'exécution d'une tâche parallèle.
TâcheParallèleAttendToutesAttend la fin de l'exécution de toutes les tâches parallèles présentes dans un tableau.
La propriété ValeurRenvoyée de la variable de type TâcheParallèle permet de connaître la valeur renvoyée par la procédure exécutée par la tâche parallèle.
Attention : Cette valeur est disponible uniquement si la tâche parallèle est terminée. Si la tâche est en cours, l'appel de cette propriété est bloquant jusqu'à la fin de la tâche.

Gérer l'enchaînement des tâches parallèles

Plusieurs tâches parallèles peuvent être lancées simultanément. Il est possible de définir l'enchaînement des tâches parallèles : une tâche peut attendre la fin de l'exécution d'une ou plusieurs tâches avant de s'exécuter. Les fonctions suivantes permettent de définir une tâche de continuation :
TâcheParallèleExécuteAprèsIndique une tâche parallèle de continuation qui sera exécutée lorsque la tâche parallèle spécifiée sera terminée.
TâcheParallèleExécuteAprèsToutesIndique une tâche parallèle de continuation qui sera exécutée lorsque toutes les tâches d'un tableau de tâches parallèles seront terminées.
TâcheParallèleExécuteAprèsUneIndique une tâche parallèle de continuation qui sera exécutée après la première tâche terminée d'un tableau de tâches parallèles.
Remarque : Dans une tâche de continuation, il est possible :

Manipuler les champs depuis une tâche parallèle

Il n'est pas possible d'agir sur l'interface depuis une tâche parallèle. Il n'est donc pas possible d'affecter un champ, de remplir un champ Table ou Zone répétée.
Seule une tâche définie avec la constante tpoThreadPrincipal pourra s'exécuter dans le thread principal et pourra si nécessaire mettre à jour les champs.
Remarque : Il est également possible d'utiliser la fonction ExécuteThreadPrincipal pour exécuter une procédure d'affichage spécifique depuis la tâche parallèle.
Ces deux méthodes sont utilisée dans l'exemple Améliorer la réactivité de l'application présenté dans cette page.
Exemple : Accélérer les traitements

Accélérer les traitements : calcul de statistiques

Une application utilise la procédure CalculeStatAdresseEmail pour calculer des statistiques d'envoi et de réception sur chaque adresse email du fichier CLIENT. Cette procédure prend en paramètre l'adresse email et calcule toutes les statistiques sur cette adresse.
Si le calcul de statistiques sur une adresse email prend une seconde et si la base de données contient 200 000 adresses email, le calcul prend plus de deux jours (200 000 secondes).
Pour aller plus vite, il suffit de lancer une tâche parallèle pour chaque adresse email trouvée.
Exemple de code :
  • Code initial (avant l'utilisation des tâches parallèles) :
    nAdressesEnErreur est un entier
     
    Sablier(Vrai)
    ChronoDébut()
    // Parcours la liste des clients
    POUR TOUT Client
    // Lance le calcul de statistiques sur son adresse email
    SI CalculeStatAdresseEmail(Client.Email, 1) = Faux ALORS
    nAdressesEnErreur++
    FIN
    FIN
    Sablier(Faux)
    LIB_Résultat_1 = ChaîneConstruit("Résultat : %1 adresses en erreur", nAdressesEnErreur)
    Info("Traitement terminé", DuréeVersChaîne(ChronoFin(), "MMm SSs CCC"))
  • Code utilisant les tâches parallèles :
    nAdressesEnErreur est un entier
    tabTâches est un tableau de TâchesParallèles
    UneTâche est une TâcheParallèle
     
    Sablier(Vrai)
    ChronoDébut()
    // Parcours la liste des clients
    POUR TOUT Client
    // Lance le calcul de statistiques sur son adresse email à l'aide d'une tâche parallèle
    UneTâche = TâcheParallèleExécute(CalculeStatAdresseEmail, ...
    (Client.Email, 1), tpoCopieLégèreContexteHFSQL)
    // Mémorise cette tâche dans un tableau
    Ajoute(tabTâches, UneTâche)
    FIN
     
    // Attend la fin de l'exécution des tâches
    TâcheParallèleAttendToutes(tabTâches)
    Sablier(Faux)
     
    // Parcours les tâches
    POUR TOUT UneTâche DE tabTâches
    SI UneTâche..ValeurRenvoyée = Faux ALORS
    nAdressesEnErreur++
    FIN
    FIN
    LIB_Résultat_2 = ChaîneConstruit("Résultat : %1 adresses en erreur", nAdressesEnErreur)
    Info("Traitement terminé", DuréeVersChaîne(ChronoFin(), "MMm SSs CCC"))
Exemple : Améliorer la réactivité de l'application

Améliorer la réactivité de l'application

Un champ Table affiche une liste de contacts dont la photo est chargée depuis une adresse Internet. Pour chaque contact, l'application fait une requête Internet (ce qui entraîne un ralentissement).
Pour améliorer la réactivité de l'application, et obtenir une IHM fluide, une tâche parallèle est lancée dans le traitement d'affichage d'une ligne du champ Table. Cette tâche parallèle :
  • reçoit en paramètre l'identifiant du contact.
  • fait la requête Internet pour obtenir l'image.
  • récupère l'image.
  • appelle une fonction pour mettre à jour le champ Table.
Pour cet exemple, voici deux codes différents : ces deux codes présentent deux façons différentes de mettre à jour l'IHM :

Mise à jour l'IHM via une tâche de continuation

La mise à jour de l'interface n'étant pas possible depuis une tâche parallèle exécutée dans le thread principal, une tâche de continuation spécifique pour l'affichage est mise en place via la fonction TâcheParallèleExécuteAprès. Un des paramètres passés à la tâche de continuation correspond à la valeur renvoyée par la tâche parallèle principale. Pour spécifier ce paramètre, il suffit d'utiliser le mot-clé ValeurRenvoyéeTâchePrécédente.
Exemple de code :
  • Code d'affichage d'une ligne de la table :
    // Si la photo n'est pas encore renseignée
    SI COL_Photo ~= "" ALORS
    // Positionne l'image de sablier en attendant de récupérer la photo depuis "Internet"
    COL_Photo = IMG_Sablier
    // Lance la récupération de la photo dans une tâche parallèle
    MaTacheRechercheImage est une TâcheParallèle = TâcheParallèleExécute(RechercheImage, ...
    (COL_NumClient), tpoCopieLégèreContexteHFSQL)
    // Lance l'affichage de l'image dans une tâche de continuation
    // qui interagit avec l'interface
    TâcheParallèleExécuteAprès(MaTacheRechercheImage, AfficheImage, ...
    (COL_NumClient, ValeurRenvoyéeTâchePrécédente), tpoThreadPrincipal)
    FIN
  • Code de la procédure "RechercheImage" : Ce traitement permet de récupérer l'image.
    PROCÉDURE RechercheImage(LOCAL nIDClient est un entier sur 8)
     
    // Récupération de la photo
    Résultat1 est un booléen = HTTPRequête("http://Linkedin.com/photos/id=" + ID)
    SI Résultat1 = Vrai ALORS
    bufPhoto est un Buffer = HTTPDonneRésultat()
    bufPhoto = fChargeBuffer(fRepExe() + fSep() + "Photos\" + ID + ".jpg")
    FIN
     
    RENVOYER bufPhoto
  • Code de la procédure "AfficheImage" : Cette procédure permet d'afficher l'image dans la table.
    PROCÉDURE AfficheImage(nIDClient est un entier sur 8, sCheminPhoto est une chaîne)
    // Recherche le client dans la table
    nIndice est un entier = TableCherche("FEN_MENU.TABLE_Client.COL_NumClient", nIDClient)
    SI nIndice > 0 ALORS
    // Affiche la photo du client
    FEN_Menu.TABLE_Client.COL_Photo[nIndice] = sCheminPhoto
    FIN

Mise à jour l'IHM via une procédure exécutée dans le thread principal

Depuis une tâche parallèle, il est interdit d'accéder aux champs de la fenêtre. Pour afficher l'image, la procédure AfficheImage est exécutée via la fonction WLangage ExécuteThreadPrincipal. Cette fonction force l'exécution d'une procédure dans le thread principal.
Plus simple encore, il est possible d'indiquer à la procédure AfficheImage qu'elle s'exécutera toujours dans le thread principal. Il suffit de cliquer sur le bouton dans le bandeau de l'éditeur de code et de cocher "Exécuter dans le thread principal".
Exemple de code :
  • Code d'affichage d'une ligne de la table :
    // Si la photo n'est pas encore renseignée
    SI COL_Photo ~= "" ALORS
    // Positionne l'image de sablier en attendant de récupérer la photo depuis "Internet"
    COL_Photo  = IMG_Sablier
    // Lance la récupération de la photo dans une tâche parallèle
    MaTacheRechercheImage est une TâcheParallèle = TâcheParallèleExécute(RechercheImage, ...
    (COL_NumClient), tpoCopieLégèreContexteHFSQL)
    FIN
  • Code de la procédure "RechercheImage" : Ce traitement permet de récupérer l'image.
    PROCÉDURE RechercheImage(LOCAL nIDClient est un entier sur 8)
     
    // Récupération de la photo
    Résultat1 est un booléen = HTTPRequête("http://Linkedin.com/photos/id=" + ID)
    SI Résultat1  = Vrai ALORS
    bufPhoto est un Buffer = HTTPDonneRésultat()
    bufPhoto = fChargeBuffer(fRepExe() + fSep() + "Photos\"+ ID + ".jpg")
    FIN
     
    // Appelle la procédure pour afficher l'image
    ExécuteThreadPrincipal(AfficheImage, nIDClient, bufPhoto)
  • Code de la procédure "AfficheImage" : Cette procédure permet d'afficher l'image dans la table.
    PROCÉDURE AfficheImage(nIDClient est un entier sur 8, sCheminPhoto est une chaîne)
    // Recherche le client dans la table
    nIndice est un entier = TableCherche("FEN_MENU.TABLE_Client.COL_NumClient", nIDClient)
    SI nIndice > 0 ALORS
    // Affiche la photo du client
    FEN_Menu.TABLE_Client.COL_Photo[nIndice] = sCheminPhoto
    FIN
Version minimum requise
  • Version 20
Documentation également disponible pour…
Commentaires
Cliquez sur [Ajouter] pour publier un commentaire

Dernière modification : 25/05/2022

Signaler une erreur ou faire une suggestion | Aide en ligne locale