![]() |
guill.net
-
La page des réseaux
|
![]() |
|
![]() ![]() ![]() |
Troisième partie : Utilisation d’un THREAD et gestion des exceptions
Lancement d’une tâche de « fond » : utilisation d’un THREAD
Dans cette partie nous aborderons (très simplement) la notion de thread et comment la mettre en œuvre.
Un thread est « équivalent à une tache de fond » qui tourne en permanence au sein du thread principal qui est votre application.
A titre d’exemple, un tableur doit recalculer la feuille de calcul à chaque nouvelle saisie. Le thread principal est la saisi, auquel on associe un thread secondaire de priorité inférieure le recalcul de la feuille qui sera donc exécuter automatiquement et uniquement quand l’utilisateur ne fait aucune saisie dans la feuille de calcul.
Pour Créer un thread en C++ Builder, sélectionner dans l’option Fichier/Nouveau l’icône objet Thread. Donner un nom à la classe, par exemple ThreadNomDuProcess. Un fichier est créé par C++ Builder du Type :
#include <vcl.h>
#pragma hdrstop
#include "UnitThreadNomDuProcess.h"
#pragma package(smart_init)
__fastcall ThreadCompteur::
ThreadNomDuProcess (bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
void __fastcall ThreadNomDuProcess::Execute()
{
//---- Placez le code du thread ici ----
}
//---------------------------------------------------------------------------
Execute doit tester la valeur de la propriété Terminated afin de déterminer s'il faut sortir du thread. Typiquement le code de la méthode Execute doit être du type :
void __fastcall ThreadNomDuProcess::Execute()
{
//---- Placez le code du thread ici ----
while ( !Terminated)
// actions répétitives à faire
}
//---------------------------------------------------------------------------
Un thread commence lorsque Create est appelée avec le paramètre CreateSuspended initialisé à false, ou si la méthode Resume est appelée après un appel de Create dans lequel CreateSuspended est initialisé à true.
Pour exécuter un thread il suffit de lancer la méthode Resume() : Reprend l'exécution d'un thread interrompu.
Pour Arrêter un thread il suffit de lancer la méthode Terminate() : Signale au thread de s'arrêter en affectant la valeur true à la propriété Terminated.
Terminate initialise la propriété Terminated du thread à true, en signalant que le thread doit se terminer dès que possible. A l'inverse de l'API Windows TerminateThread, qui force le thread à se terminer immédiatement, la méthode Terminate demande simplement que le thread se termine. Ceci permet au thread d'exécuter tout nettoyage avant de se fermer.
Pour que Terminate fonctionne, la méthode Execute du thread et toute méthode appelée par Execute doit tester périodiquement Terminated et quitter lorsqu'elle vaut true.
Prenons un exemple : Nous voulons réaliser un programme dans lequel
Créer l’interface
utilisateur suivante :
Choisir fichier/Nouveau projet et enregistrer le avec les noms par défauts sur le répertoire voulu.
Ajouter un objet Thread au
projet : Fichier/Nouveau choisir objet thread. Donner lui comme nom de
classe ThreadCompteur. Enregistrer le fichier sous le répertoire
voulu avec comme nom UnitThreadCompteur.cpp
Ecrivez le code suivant
: dans Unit1.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "UnitThreadCompteur.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
ThreadCompteur *ThCompteur;
// définition d'un pointeur sur le thread
/*----------------------------------------------------------------------*/
/* Création du
process de comptage
*/
/*----------------------------------------------------------------------*/
void ProcessCompteur_Create(void)
{
ThCompteur=new
ThreadCompteur(true); // resevation de la mémoire
ThCompteur->Priority
= tpLower; // priorité inférieure
ThCompteur->Resume();
// Lancement du traite
}
/*-----------------------------------------------------------------------*/
/* désactivation
du process de Comptage
*/
/*-----------------------------------------------------------------------*/
void ProcessCompteur_Kill(void)
{
ThCompteur->Terminate();
// arrêt du thread
delete ThCompteur;
// libération mémoire
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent*
Owner)
: TForm(Owner)
{
ProcessCompteur_Create();
// lancement de la tâche d'acquisition au
// démarage du programme
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject
*Sender)
{
Label1->Caption
= Edit1->Text; // recopie du test si action sur le Bouton
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject
*Sender, TCloseAction &Action)
{
ProcessCompteur_Kill();
// arrêter le thread en fin de programme
}
//---------------------------------------------------------------------------
Ecrivez le code suivant : dans UnitThreadCompteur.cpp
#include <vcl.h>
#pragma hdrstop
#include "UnitThreadCompteur.h"
#include "Unit1.h"
#pragma package(smart_init)
__fastcall ThreadCompteur::ThreadCompteur(bool
CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
// code du thread :
action à réaliser
//---------------------------------------------------------------------------
void __fastcall ThreadCompteur::Execute()
{
int Compteur;
Compteur = 0; // inialisation du compteur
while (!Terminated)
// test si demande d'arrêt du thread
{
Compteur++;
Compteur=Compteur%60;
Sleep(1000);
Form1->LabelCompteur->Caption
= StrToInt(Compteur);
Form1->Label1->Caption
= Form1->Edit1->Text;
}
}
En exécutant le programme le compteur s’incrémentera toutes les secondes et son contenu sera affiché à l’écran .De même la donnée saisie par l’utilisateur sera recopiée toutes les secondes.
Gestion des exceptions
Une exception est assimilable
à une erreur. L’exemple la plus simple est la division par 0. Un
programme correctement écrit doit donc gérer tous les types
d’erreurs susceptibles d’engendrer des disfonctionnements et de «
planter » le programme.
Dans cette partie nous décrirons
une méthode permettant de gérer des exceptions.
Les exceptions sont déclenchées quand une erreur d'exécution se produit dans une application, par exemple une tentative de division par zéro. Généralement, quand une exception est déclenchée, une instance d'exception affiche une boîte de dialogue décrivant la condition d'erreur. Si une application ne traite pas la condition d'exception, le gestionnaire d'exception par défaut est appelé. Ce gestionnaire affiche également une boîte de dialogue avec un bouton OK qui permet normalement à une application de poursuivre les traitements quand l'utilisateur clique sur OK.
L'objet Exception offre une interface homogène aux conditions d'erreur et permet aux applications de gérer les conditions d'erreur d'une manière élégante. Les applications peuvent , intercepter et gérer des exceptions spécifiques dans des blocs try..catch.
La gestion d’une exceptions à l’aide des blocs try et catch et de la forme gènerale :
try
{
// Ecrire le
code où une exception est susceptible de se produire
}
catch(…)
{
// Gérer
l’exception
}
L’exemple ci dessous illustre
la gestion d’une exception, lors d’un calcul. L’utilisateur saisi 2 nombres
A et B et une action sur le bouton A/B calcul et affiche le résultat
de la division de A par B.
Créer un nouveau
projet et donner les noms UnitDivision .cpp pour le code et ProjectDivision
pour le nom de projet.
Créer l’écran
utilisateur suivant :
La division impose que :
- A et B soit des nombres
- B soit différent
de 0
Si ces 2 conditions ne sont pas respectées le programme générera une erreur. L’objectif est d’intercepter ces erreurs.
Nous devons donc contrôler
que A et B sont des nombres pour cela nous décrirons 2 méthodes
pour gérer ces exceptions :
- Pour A nous utiliserons
les message d’erreur du système
- Pour B nous définirons
notre message d’erreur
Nous devons contrôler lors de la division que B est différent de 0. Dans le cas ou B est différent de 0 nous n’afficherons pas de message d’erreur mais nous donnerons comme résultat DIV/0.
Ecrivez le code ci après dans le fichier UnitDivision.cpp :
#include <vcl.h>
#pragma hdrstop
#include "UnitDivision.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent*
Owner)
: TForm(Owner)
{
// effacement
des données
EditA->Text =
"";
EditB->Text =
"";
Edit1->Text =
"";
}
//---------------------------------------------------------------------------
// Gestion de la saisi
sur A
//---------------------------------------------------------------------------
void __fastcall TForm1::EditAChange(TObject
*Sender)
{
float Nb;
// Nous ne voulons
saisir que des nombres
// Une execption
peut se produire => prévoir sa gestion
try
{
// Nous
vérifions que la chaine est différente de chaine vide
if (StrLen(EditA->Text.c_str())!=0)
Nb
= StrToFloat(EditA->Text.c_str()); // Déclenchement de l'exception
}
// si cconvertion échoue
catch(Exception
&UneException) // il y à une exception afficher un message
{
// Afficher
le message d’erreur
Application->ShowException(&UneException);
// Traitement
si nécessaire, dans notre cas remise à 0
EditA->Text
= "0";
}
}
//---------------------------------------------------------------------------
// Gestion de la saisi
sur B
//---------------------------------------------------------------------------
void __fastcall TForm1::EditBChange(TObject
*Sender)
{
float Nb;
char *Chaine;
Chaine = new char[100];
// Nous ne voulons
saisir que des nombres
// Une exception
peut se produire => prévoir sa gestion
try
{
// Nous
vérifions que la chaine est différente de chaine vide
if (StrLen(EditB->Text.c_str())!=0)
Nb
= StrToFloat(EditB->Text.c_str()); // Déclenchement de l'exception
}
// si convertion échoue
catch(Exception
&UneException) // il y à une exception
{
// Afficher
notre message et le type d'erreur systeme
UneException.Message
= " : Erreur de saisi pour nombre B";
ShowMessage(
AnsiString(UneException.ClassName())+UneException.Message);
// Traitement
si nécessaire, dans notre cas suppression
// du dernier
caractère saisi
AnsiString(Chaine)
= EditB->Text;
Nb = StrLen(Chaine.c_str());
Chaine[Nb]='\0';
// fin de chaine
EditB->Text
= Chaine;
}
// Libération
mémoire
delete[] Chaine;
}
//---------------------------------------------------------------------------
// Gestion du calcul
de A / B
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject
*Sender)
{
int BestNul;
float r;
// Nous sommes
sûr que A et B sont des nombres ou une chaîne vide
// il nous reste
à vérifier que A et B sont différents de chaîne
vide
// et que B n'est
pas égale à 0
// Si A et B différent
de chaine vide on peut faire par exemple
if ( (StrLen(EditA->Text.c_str())!=0)&&(StrLen(EditB->Text.c_str())!=0))
{
// Il nous
faut vérifier que B n'est pas nul. Nous le gérerons pour
la forme
// par
une exception. Vous pouvez le gérer autrement bien sûr
try
{
1/StrToFloat(EditB->Text.c_str()); // exception ?
BestNul = 0; // B n'est pas nul
}
catch(...)
{
// Pas d'affichage de message, Mise à jour d'un flag d'erreur
BestNul = 1; // B est nul
}
//
Faire le calcul si possible
if
(BestNul == 0)
{
r=EditA->Text.ToDouble()/EditB->Text.ToDouble();
Edit1->Text = FloatToStr(r);
}
else
Edit1->Text = "DIV/0";
} // fin si chaîne
vide
else // A ou/et
B sont vide
{
// Faire
ce que vous voulez
}
}
//---------------------------------------------------------------------------
Conclusion
La programmation sous Windows est extraordinairement puissante, et nécessite un foule de connaissances à acquérir, mais elle tend à devenir un standard du marché.
Les différents point
abordés dans cette présentation ne sont que des bases, vous
permettant de créer rapidement quelques applications. Une utilisation
régulière et approfondie vous permettra certainement d’utiliser
ces bases autrement et certainement plus simplement.