IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Delphi Discussion :

D10.4.2 - Réutiliser un Thread


Sujet :

Langage Delphi

  1. #1
    Membre éprouvé
    Avatar de Andry
    Profil pro
    Informaticien
    Inscrit en
    Juillet 2002
    Messages
    1 164
    Détails du profil
    Informations personnelles :
    Localisation : Madagascar

    Informations professionnelles :
    Activité : Informaticien

    Informations forums :
    Inscription : Juillet 2002
    Messages : 1 164
    Points : 1 181
    Points
    1 181
    Par défaut D10.4.2 - Réutiliser un Thread
    Bonjour à tous,
    Voici le topo.
    J'ai un TFrame avec un LookupComboBox contenant une liste de clients.
    à Chaque sélection de client depuis le LookupCombobox, je lance une requête SQL afin de ramener les informations nécessaire concernant ce client.
    Dès fois, suivant la quantité des informations et le réseau, la requête prends plus de temps et freeze la fenêtre de l'application.
    Donc j'ai décidé de déporter l'exécution de la requête dans un TThread et récupère les résultat comme ceci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
     
    TUniQueryThread = class(TThread)
      private
        FConnection : TUniConnection;
        FQuery      : TUniQuery;
        FFrame      : TFrameCustomer;
        FSQL        : String;
      published
        property Query      : TUniQuery read FQuery;
        property Connection : TUniConnection read FConnection;
        property Frame      : TFrameCustomer read FFrame;
        property SQL        : String read FSQL write FSQL;
     
        constructor Create(AFrame : TFrameCustomer);
        destructor Destroy; override;
        procedure BuildSQL;
        procedure Execute; override;
        procedure TransferData;
      end;
     
    .....
    .....
      procedure TUniQueryThread.Execute;
    begin
      CoInitialize(nil);
      try
         BuildSQL;
         FQuery.Open;
         Synchronize(TransferData);
      finally
        CoUninitialize;
      end;
    end;
     
    .....
    .....
    Donc ce que je fait dès qu'il y a sélection de client
    1. Vérifier que ce n'est pas le même client
    2. Créer le Thread et l’exécute
    3. Récupère les données via TransferData
    4. Le thread est automatiquement détruit à la fin.

    Je trouve que ceci fait beaucoup de create/free du coup je me suis demander s'il est possible de
    1. Créer un thread lors de la création de la Frame
    2. Réutiliser le Thread à chaque sélection de client
    3. Détruire le thread lors de la destruction de la Frame.


    Ceci est un domaine que je ne maitrise pas encore donc je compte sur votre lumière.
    Merci à vous
    Andry

  2. #2
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 563
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 563
    Points : 25 165
    Points
    25 165
    Par défaut
    Non, entre le Terminated et surtout le Finished, un état définitif il me semble.


    L'autre approche est d'avoir un thread permanent qui attend avec un TEvent WaitFor et une FIFO (TThreadList, TThreadQueue<>)

    Même si pour paralléliser du SQL, ta méthode me semble suffisante, j'ai eu des programmes qui faisaient cela 200 fois de suite pour avoir une progression fictive durant les requêtes longues et une progression réelle en fonction du nombre d'étape.
    Voir mes propos sur ce programme ICI où c'était la propriété NonBlocking qui définissait si ExecSQL était en direct ou en thread

  3. #3
    Membre éprouvé
    Avatar de Andry
    Profil pro
    Informaticien
    Inscrit en
    Juillet 2002
    Messages
    1 164
    Détails du profil
    Informations personnelles :
    Localisation : Madagascar

    Informations professionnelles :
    Activité : Informaticien

    Informations forums :
    Inscription : Juillet 2002
    Messages : 1 164
    Points : 1 181
    Points
    1 181
    Par défaut
    Merci ShaiLeTroll.
    Je vais donc laisser le programme dans l’état ou elle est actuellement.
    Sinon, je vais juste bloquer le LookupComboBox pendant l’exécution du Thread et le débloquer dans Sychronize afin de ne pas lancer x threads en simultanées.
    Vaut mieux faire simple que compliqué s'il n'y a pas de gain.

    Andry

  4. #4
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 754
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 754
    Points : 13 340
    Points
    13 340
    Par défaut
    On ne voit pas la création des composants dans Execute. Es-tu sûr que ces compos "Uni" sont thread-safe ?

  5. #5
    Membre éprouvé
    Avatar de Andry
    Profil pro
    Informaticien
    Inscrit en
    Juillet 2002
    Messages
    1 164
    Détails du profil
    Informations personnelles :
    Localisation : Madagascar

    Informations professionnelles :
    Activité : Informaticien

    Informations forums :
    Inscription : Juillet 2002
    Messages : 1 164
    Points : 1 181
    Points
    1 181
    Par défaut
    Bonjour Andnotor,
    Citation Envoyé par Andnotor Voir le message
    On ne voit pas la création des composants dans Execute.
    La n'est pas la question, je crée un thread à chaque traitement, et je me demandais s'il etait possible de n'utiliser qu'un seul.
    Ci-dessous les codes si tu veux
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
     
    Type
      TFrameCustomer = class;
     
      TUniQueryThread = class(TThread)
      private
        FConnection : TUniConnection;
        FQuery      : TUniQuery;
        FFrame      : TFrameCustomer ;
        FStartDate  : Nullable<TDate>;
        FEndDate    : Nullable<TDate>;
        FCustNo     : Integer;
      published
        property Query      : TUniQuery read FQuery;
        property Connection : TUniConnection read FConnection;
        property Frame      : TFrameCustomer read FFrame;
        property StartDate  : Nullable<TDate> read FStartDate;
        property EndDate    : Nullable<TDate> read FEndDate;
        property CustNo     : Integer read FCustNo;
     
        constructor Create(AFrame   : TFrameCustomer;
                           ACustNo  : Integer;
                           AStartDate,
                           AEndDate : Nullable<TDate>);
        destructor Destroy; override;
        procedure BuildSQL;
        procedure Execute; override;
        procedure TransferData;
      end;
     
      TFrameCustomer = class(TFrameBase)
     
      .....
     
    {*******************************************************************************
                              TUniQueryThread
    *******************************************************************************}
    constructor TUniQueryThread.Create(AFrame   : TFrameCustomer;
                                       ACustNo  : Integer;
                                       AStartDate,
                                       AEndDate : Nullable<TDate>);
    begin
      inherited Create(True);
      FreeOnTerminate           := True;
      FFrame                    := AFrame;
      FCustNo                   := ACustNo;
      FStartDate                := AStartDate;
      FEndDate                  := AEndDate;
      FConnection               := TUniConnection.Create(Nil);
      FConnection.ConnectString := ModuleData.DbConnection.ConnectString;
      FConnection.LoginPrompt   := False;
     
      FQuery                    := TUniQuery.Create(Nil);
      FQuery.Connection         := FConnection;
    end;
    {------------------------------------------------------------------------------}
    destructor TUniQueryThread.Destroy;
    begin
      FQuery.Free;
      FConnection.Free;
      inherited;
    end;
    {------------------------------------------------------------------------------}
    procedure TUniQueryThread.Execute;
    begin
      CoInitialize(nil);
      try
        BuildSQL;
        FQuery.Open;
        Synchronize(TransferData);
      finally
        CoUninitialize;
      end;
    end;
    {------------------------------------------------------------------------------}
    procedure TUniQueryThread.TransferData;
    begin
      FFrame.MemDoc.DisableControls;
      try
        FFrame.MemDoc.Close;
        FFrame.MemDoc.LoadFromDataSet(FQuery);
        FFrame.LastCustNo := FCustNo;
      finally
        FFrame.MemDoc.EnableControls;
      end;
    end;
    {------------------------------------------------------------------------------}
    Et voici le code d'appel
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    var
      fThread : TUniQueryThread;
      fStart,
      fEnd    : Nullable<TDate>;
    begin
      if VarIsNullDate(DEditStart.Date) then
        fStart := Null
      else
        fStart := DEditStart.Date;
      if VarIsNullDate(DEditEnd.Date) then
        fEnd := Null
      else
        fEnd := DEditEnd.Date;
      LookupCustomer.Enabled := False;
      ActivityIndicator.Visible := True;
      ActivityIndicator.Active  := True;
      fThread := TUniQueryThread.Create(Self,
                                        LookupCustomer.EditValue,
                                        fStart,
                                        fEnd);
      fThread.Start;
    Citation Envoyé par Andnotor Voir le message
    Es-tu sûr que ces compos "Uni" sont thread-safe ?
    Oui d'après DevArt.
    Andry

  6. #6
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 448
    Points
    28 448
    Par défaut
    tu peux concevoir un système de worker...c'est à dire un Thread qui tourne en permanence est qui attend une tâche à exécuter.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    procedure TWorker.Execute;
    begin
      whlie not Terminated do
      begin
        Synchronize(GetTask);
        if FTask <> nil then
        begin
          FTask.BuildSQL(Self);
          FQuery.Open;
          Synchronize(TransferData);
          FQuery.Close; // probablement
        end;
      end;
    end;
     
    procedure TWorker.GetTask;
    begin
      if FTasks.Count = 0 then
        FTask := nil 
      else begin
        FTask := FTasks[0];
        FTasks.Delete(0);
      end;
    end;
     
    constructor TTask.Create(Worker: TWorker);
    begin
      Worker.FTasks.Add(Self);
      ...
    end;
    tu peux mettre tout ce qui est composants SQL dans TWorker, et TTask ne contient que les paramètres

    tu peux aussi utiliser FTask pour le TransfertData, du coup tu crée autant de Task que tu as de requêtes différentes et le système fonctionnera à l'identique.

  7. #7
    Membre éprouvé
    Avatar de Andry
    Profil pro
    Informaticien
    Inscrit en
    Juillet 2002
    Messages
    1 164
    Détails du profil
    Informations personnelles :
    Localisation : Madagascar

    Informations professionnelles :
    Activité : Informaticien

    Informations forums :
    Inscription : Juillet 2002
    Messages : 1 164
    Points : 1 181
    Points
    1 181
    Par défaut
    Merci Paul TOTH
    Je vais mettre au chaud ton idée car il y a un appli qui nécessite vraiment ce genre de système.
    Pour celui ci, je vais me tenir à mon code original; qui est assez simple et qui fait déjà l'affaire; vu qu'on a pas vraiment de gain mais plus de complexité au niveau code.
    Merci à vous tous de m'avoir montrer le chemin, je commence à m'y intéresser car j'ai des applications déjà fonctionnelles qui permet de sortir des tas de rapport (requête SQL) mais qui des fois se freeze et Windows l’arrête. Donc la solution sera l'utilisation du worker et Task.
    Andry

  8. #8
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2016
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2016
    Messages : 15
    Points : 15
    Points
    15
    Par défaut
    En utilisant une instance de TStringList que tu alimentes en ajoutant une nouvelle requête par la fin et tu traites la requête en tête de liste.
    Un seul Thread comme Paul l’a indiqué, et un code qui ressemblerait à ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    type
      TWorkerThread = class(TThread)
      private
        ThList: TStringList;
        ThLock: Boolean;
        ThFacade: type_composant;
        procedure Process;
      protected
        procedure Execute; override;
      public
        constructor Create(const AList: TStringList; const AFacade: type_composant);
      end;
    et

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
     
    implementation
     
    { TWorkerThread }
     
    constructor TWorkerThread.Create(const AList: TStringList; ; const AFacade: type_composant);
    begin
      inherited Create(False); //Le thread est lancé dès la création de l'instance
      FreeOnTerminate := True; //L'instance est libérée avec le terminate
      Priority := tpLowest;    //Priorité du thread
      ThList := AList;
      ThLock := False;
      ThFacade := AFacade;
    end;
     
    procedure TWorkerThread.Execute;
    begin
      while not Application.Terminated and not Terminated do begin
        Synchronize(Process);
        Sleep(20);
      end;
    end;
     
    procedure TWorkerThread.Process;
    begin
      if not ThLock  and (ThList.Count > 0) then begin
        ThLock := True;
        try
          var SQLText := ThList.Strings[0];
          //Traiter la requête
          //Afficher le résultat via ThFacade
          ThList.Delete(0);
        finally
          ThLock := False;
        end;
      end;
    end;

Discussions similaires

  1. Réponses: 4
    Dernier message: 29/04/2020, 12h00
  2. Réutilisation d'un thread
    Par ToTo13 dans le forum Général Java
    Réponses: 4
    Dernier message: 15/06/2010, 16h16
  3. Tri multi-threadé
    Par Tifauv' dans le forum C
    Réponses: 8
    Dernier message: 28/06/2007, 09h00
  4. [Kylix] Pb de Thread !!
    Par Anonymous dans le forum EDI
    Réponses: 1
    Dernier message: 25/04/2002, 13h53

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo