Developpez.com - C
X

Choisissez d'abord la catégorieensuite la rubrique :



TThread

Par CGi

Le 21 septembre 2003



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 monoprocesseur) "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.)

        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 ne va pas 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 faîtes "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 :

#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 :

#include <vcl.h>
//#include <shellapi.h> // nécéssairesous 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é.";
}

Cette fois la fonction WaitForSingleObject bloquera uniquement le Thread et pas l'application. Nous y ajouterons une méthode nommé AffMessage qui nous changera le texte d'un Label se trouvant sur la fiche principale de l'application. Ceci juste pour mettre en oeuvre ce qui est expliqué en commentaire dans le code généré de "Unit2.cpp". Cette méthode sera appellé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éthode ou des propriété 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 :

#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 :

#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éclanché 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 s'autorisons à quitter l'application seulement si le thread est terminé, car si le clavier et la souris n'est plus actif pour l'application, le clique droit sur le bouton de l'application dans la barre des taches et lui encore accéssible 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 à é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





C/C++
  Les pointeurs du C/C++.   Les listes chaînées.             Liste simple.             Liste triée.             Liste double.   Les arbres.   Les tas.   Le C orienté objets ?

  1 - La fenêtre principale.   2 - Contrôles et messages.   3 - Les commandes.   4 - Dialogue std.   5 - Contexte de périph.   6 - Dessiner.   7 - Les ressources.   8 - Dialogue perso.   9 - Dialogue comm.   10 - Les accélérateurs.

Assembleur
  Assembleur sous Visual C++.

C++ BUILDER
  Trucs et astuces.   Composant.   TRichEdit.   TDrawGrid.   Application MDI.   TThread.   wxWidgets.   Style Win XP.

  Première application.   Construire un menu.   Dessiner.   Sisers, Timers...   Dialogues standards.   Dialogues perso.

DotNet
  Composant C# Builder.   Contrôle WinForm.   Application MDI.

Java
  Applet java.





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.

Contacter le responsable de la rubrique C