TThread

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Création d'un thread avec la classe TThread

Ce tutoriel va vous présenter sous forme d'un exemple, comment créer un thread avec la classe TThread de la VCL de C++Builder.

Tout d'abord qu'est qu'un thread ?

On peut voir ça comme une partie de code qui s'exécute en parallèle au code principal de notre application, qui en fait est aussi un thread. Le système d'exploitation (sous les systèmes monoprocesseurs) « partage » le temps processeur entre les différents threads en donnant régulièrement accès à chacun d'eux au processeur pendant un très court instant, ce qui nous donne l'impression que les deux parties de code s'exécutent simultanément. Cela peut être utile pour exécuter des processus lents sans bloquer l'application.

Pour aborder la classe Tthread, nous allons prendre un sujet qui a été traité plusieurs fois sur le forum C++ Builder : Comment lancer une application externe et attendre qu'elle se termine pour redonner la main à notre application ?

Nous lancerons donc notre application externe avec le code suivant :

(Pour l'exemple ce sera la calculatrice de Windows qui en principe se trouve sur toutes les configurations.)

 
Sélectionnez
        SHELLEXECUTEINFO shInfo;
        ZeroMemory(&shInfo,sizeof(shInfo));
        shInfo.cbSize=sizeof(shInfo);
        shInfo.hwnd=NULL;
        shInfo.fMask=SEE_MASK_NOCLOSEPROCESS;
        shInfo.lpVerb=NULL;
        shInfo.lpFile="Calc.exe";
        shInfo.lpParameters=NULL;
        shInfo.nShow=SW_SHOWNORMAL;
        bool shRetour=ShellExecuteEx(&shInfo);
        if (shRetour) WaitForSingleObject(shInfo.hProcess,INFINITE);

Dans ce code la méthode WaitForSingleObject bloque l'application tant que l'application appelée n'est pas terminée. Ce qui a pour effet néfaste que le dessin de notre application n'est plus rafraîchi. Il va sans dire que ce n'est pas très esthétique. Donc pour remédier à cet inconvénient, nous allons lancer l'application externe dans un thread et nous désactiverons les événements souris et clavier de notre application tant que le thread n'est pas terminé. Par contre le dessin de la fenêtre de notre application sera toujours opérant.

Pour l'exemple créons une nouvelle application, puis créons le thread, pour cela faites « Nouveau » puis sur l'onglet « Nouveau » : « Objet Thread », saisir le nom de sa classe dans la boîte de dialogue prévue à cet effet et faire « OK », pour l'exemple nous l'appellerons « TMonThread ». Maintenant nous avons nos fichiers Unit2.h et Unit2.cpp contenant le code minimum de notre thread.

Il ne reste plus qu'à ajouter le code pour lancer l'application externe dans la méthode execute du thread.

Fichier Unit2.h :

 
Sélectionnez
#ifndef Unit2H
#define Unit2H
//---------------------------------------------------------------------------
#include <Classes.hpp>
//---------------------------------------------------------------------------
class TMonThread : public TThread
{
private:
protected:
        void __fastcall Execute();
        void __fastcall AffMessage();
public:
        __fastcall TMonThread(bool CreateSuspended);
};
//---------------------------------------------------------------------------
#endif

Fichier Unit2.cpp :

 
Sélectionnez
#include <vcl.h>
//#include <shellapi.h> // nécéssaire sous BCB4
#pragma hdrstop

#include "Unit2.h"
#include "Unit1.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------

//   Important : les méthodes et les propriétés des objets de la VCL ne peuvent être
//   utilisées que dans une méthode appelée en utilisant Synchronize, comme :
//
//      Synchronize(UpdateCaption);
//
//   où UpdateCaption serait de la forme :
//
//      void __fastcall TMonThread::UpdateCaption()
//      {
//        Form1->Caption = "Mise à jour dans un thread";
//      }
//---------------------------------------------------------------------------

__fastcall TMonThread::TMonThread(bool CreateSuspended)
        : TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
void __fastcall TMonThread::Execute()
{
        //----  Placer le code du thread ici  ----

        SHELLEXECUTEINFO shInfo;
        ZeroMemory(&shInfo,sizeof(shInfo));
        shInfo.cbSize=sizeof(shInfo);
        shInfo.hwnd=NULL;
        shInfo.fMask=SEE_MASK_NOCLOSEPROCESS;
        shInfo.lpVerb=NULL;
        shInfo.lpFile="Calc.exe";
        shInfo.lpParameters=NULL;
        shInfo.nShow=SW_SHOWNORMAL;
        bool shRetour=ShellExecuteEx(&shInfo);
        if (shRetour)
         {
          Synchronize(AffMessage);
          WaitForSingleObject(shInfo.hProcess,INFINITE);
         }
}
//---------------------------------------------------------------------------
void __fastcall TMonThread::AffMessage()
{
       Form1->Label1->Caption = "Application externe lancée.";
}

Cette fois la fonction WaitForSingleObject bloquera uniquement le Thread et pas l'application. Nous y ajouterons une méthode nommée AffMessage qui nous changera le texte d'un Label se trouvant sur la fiche principale de l'application. Ceci juste pour mettre en œuvre ce qui est expliqué en commentaire dans le code généré de « Unit2.cpp ». Cette méthode sera appelée par la méthode Synchronize dès le démarrage de l'application externe. Comme expliqué, ce mode opératoire doit être appliqué dès que l'on utilise des méthodes ou des propriétés de la VCL.

Maintenant, il faut lancer notre Thread depuis notre fenêtre principale. Pour l'exemple nous poserons sur la fiche (Form1) un bouton (Button1) et un Label (Label1). Voyons maintenant son code.

Fichier Unit1.h :

 
Sélectionnez
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include "Unit2.h"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:  // Composants gérés par l'EDI
        TButton *Button1;
        void __fastcall Button1Click(TObject *Sender);
        void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
private:  // Déclarations de l'utilisateur
        TMonThread *MonThread;
        void __fastcall FinDeMonThread(TObject *Sender);
public:    // Déclarations de l'utilisateur
        __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

Dans le fichier Unit1.h, nous incluons le fichier entête « Unit2.h » pour avoir accès à la classe TMonThread, nous déclarons un pointeur MonThread sur un objet TMonThread et nous déclarons une méthode FinDeMonThread qui sera appelée quand le thread sera terminé.

Fichier Unit1.cpp :

 
Sélectionnez
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
        MonThread = new TMonThread(false);
        MonThread->OnTerminate = FinDeMonThread;
        EnableWindow(Handle, false);
}
//---------------------------------------------------------------------------
void __fastcall  TForm1::FinDeMonThread(TObject *Sender)
{
        EnableWindow(Handle, true);
        Label1->Caption = "Thread terminé";
        delete MonThread;
        MonThread = NULL;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
      if(MonThread) Action = caNone;
}

Sur l'événement OnClick de Button1, nous créons le Thread MonThread avec l'opérateur new, puis nous affectons la méthode FinDeMonThread à l'événement OnTerminate de MonThread, événement qui est déclenché comme son nom l'indique quand le thread est terminé et puisque nous voulons que la fiche Form1 ne reçoive plus les événements clavier et souris pendant l'exécution du thread nous les désactivons avec la fonction de l'API Windows EnableWindow. Quand le Thread est terminé, la méthode FinDeMonThread est exécutée, nous réactivons le clavier et la souris pour la fiche principale et nous y détruisons le Thread. Sur l'événement OnClose de Form1 (méthode FormClose) nous nous autorisons à quitter l'application seulement si le thread est terminé, car si le clavier et la souris ne sont plus actifs pour l'application, le clic droit sur le bouton de l'application dans la barre des tâches est lui encore accessible et permet de fermer l'application.

Ce petit exemple montre comment créer un Thread avec C++ Builder, comme vous pouvez le voir la classe TThread de la VCL nous facilite encore grandement les choses.

Ce code a été élaboré sous BCB6. Je l'ai testé sous BCB4, pour que la compilation aboutisse, il a fallu inclure le fichier entête « shellapi.h » dans « Unit2.cpp ».

CGi

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Copyright 2002-2016 CGi - Tous droits réservés CGi. Toutes reproduction, utilisation ou diffusion de ce document par quelque moyen que ce soit autre que pour un usage personnel doit faire l'objet d'une autorisation écrite de la part de l'auteur, propriétaire des droits intellectuels.
Les codes sources de ce document sont fournis en l'état. L'utilisateur les utilise à ses risques et périls, sans garantie d'aucune sorte de la part de l'auteur. L'auteur n'est responsable d'aucun dommage subi par l'utilisateur pouvant résulter de l'utilisation ou de la distribution des codes sources de ce document.
De la même façon, l'auteur n'est en aucun cas responsable d'une quelconque perte de revenus ou de profits, ou de données, ou de tous dommages directs ou indirects, susceptibles de survenir du fait de l'utilisation des codes sources de ce document, quand bien même l'auteur aurait été averti de la possibilité de tels dommages. L'utilisation des codes sources de ce document vaut acceptation par l'utilisateur des termes de la licence ci-dessus.