|
|
|
|
|
- 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
Gestion des tâches parallèles
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.
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. Principe Pour mettre en place une gestion de tâches parallèles, il faut : - Créer et exécuter une tâche parallèle.
- Attendre l'exécution de la tâche et récupérer la valeur de retour.
- Gérer si nécessaire l'enchaînement des tâches parallèles.
- 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ée | Booléen | - Vrai si la tâche est annulée,
- Faux dans le cas contraire.
Cette propriété est disponible en lecture seulement. | Etat | Constante de type Entier | Etat 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. | Identifiant | Entier | Identifiant de tâche. Cet identifiant peut être utilisé par exemple à des fins de débogage. Cette propriété est disponible en lecture seulement. | Terminée | Booléen | - Vrai si la tâche est terminée,
- Faux dans le cas contraire.
Cette propriété est disponible en lecture seulement. | ValeurRenvoyée | Valeur 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 : 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ès | Indique 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èsToutes | Indique 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èsUne | Indique 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. 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
Documentation également disponible pour…
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|