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

 Delphi Discussion :

Première tentative de multithread, ouch !


Sujet :

Delphi

  1. #1
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut Première tentative de multithread, ouch !
    Bonjour à tous,

    Encore une question de ma part... Promis, un jour j'apporterai plus de réponses que de questions !

    Donc, comme le titre l'indique, j'essai depuis quelques jours de comprendre comment faire du multithread proprement.
    Jusqu'à maintenant pour éviter le "freeze" de mon application j'utilisais de très simples CreateThread.
    Mais j'aimerais maintenant pouvoir convertir mes programmes simple thread en multithread.
    J'ai beau chercher ici, sur StackOverflow, sur DelphiFR, DelphiPages, partout, impossible de trouver un exemple simple de multithread "proche" de ce que je cherche...

    Pour être plus clair, voici une boucle d'exemple très simple qui réunit à peu près tout ce dont j'ai besoin :
    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
    procedure TForm1.Button1Click(Sender: TObject);
    var
      i: integer;
      http: TIdHTTP;
      s1, s2, Output: TStringList;
    begin
      s1 := TStringList.Create;
      s2 := TStringList.Create;
      Output := TStringList.Create;
     
      http := TIdHTTP.Create;
      http.HandleRedirects := True;
      http.ReadTimeout := 20000;
      http.ConnectTimeout := 20000;
     
      s1.Text := Form1.Memo1.Text;
     
      for i := 0 to s1.Count - 1 do
      begin
        try
          s2.Text := http.Get(s1.Strings[i]);
        except
        end;
        if AnsiContainsStr(s2.Text, 'blablabla') then
          Output.Add(s1.Strings[i] + ' : Correct !')
        else
          Output.Add(s1.Strings[i] + ' : Echec !');
      end;
     
      Form1.Memo2.Text := Output.Text;
     
      s1.Free;
      s2.Free;
     
      Beep;
    end;
    Et voici à quoi ressemble l'interface :


    Imaginons que ma liste d'URLs soient gigantesque (+10k), l'opération dans une boucle seule prendrait des heures...
    Mon rêve (oui, j'en rêve presque la nuit !) serait qu'il soit possible de définir un nombre de threads maximum via le SpinEdit du bas, et que l'opération soit "divisée" en plusieurs morceaux exécutées simultanément.

    J'ai lu tellement de choses sur les threads, la synchronisation, la "critical section", ... qui fait qu'en plus d'être perdu, je suis limite effrayé

    Qu'en pensez-vous ? "Ça c'est du lourd, c'est pas pour toi !" ?
    Merci par avance !

    Beny
    Fichiers attachés Fichiers attachés

  2. #2
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    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 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Pourquoi ne pas utiliser la classe TThread ?

    Dans le constructeur, tu lui passe en paramètre l'URL et un Event à invoquer lors de l'échec\succès de l'interrogation !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    constructor THTTPPing.Create(const AURL: string; AEvent: THTTPPingEvent);
    begin
      inherited Create(false);
     
      FreeOnTerminate := true;
      FEvent := AEvent; // membre privé
      FUrl := AURL; // membre privé THTTPPingEvent 
    end;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    THTTPPingEvent = procedure(Sender: THTTPPing; Success: boolean);
    Avec un FreeOnTerminate, tu n'aurais pas à te poser la question de libération !

    dans le Execute

    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
    procedure THTTPPing.Execute(); // override;
    var
      Success: boolean;
    begin
    http := TIdHTTP.Create;
      http.HandleRedirects := True;
      http.ReadTimeout := 20000;
      http.ConnectTimeout := 20000;
     
      Sucess := false; 
      try
        Sucess := AnsiContainsStr(http.Get(FUrl), 'blablabla');
      except
      end;
     
      http.free(); // ne pas oublier !!!
     
      if Assigned(FEvent) then
       FEvent(this, Sucess); // ici, voir comment tu vas synchroniser !
     
    end;
    le gestionnaire serait

    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
    procedure TForm1.HTTPPingEventHandler(Sender: THTTPPing; Success: boolean);
    var
      tmp: TList;
      P: TTruc;
    begin
      tmp := ThreadList.Acquire();
      try
        new(P);
        P.URL := Sender.URL;
        P.Success := Success; 
        tmp.Add(P);
      finally
        ThreadList.Release();
      end;
    end;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    PTruc = ^TTruc;
    TTruc = record
     URL: string;
     success: boolean;
    end;
    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
    procedure TForm1.IdleEventHandler(Sender: TObject);
    var
      P: PTruc; 
    begin
      P:= nil;
     
      tmp := ThreadList.LockList();
      try
        if tmp.count > 0 then
       begin
        P := tmp.first();
        tmp.delete(0);
       end;
     
      finally
        ThreadList.UnlockList();
      end;
     
      if Assigned(P) then
      begin
        Memo.Lines.Add(P.URL + ' : ' + BoolToStr(P.Success, true));
        Dispose(P);
      end;
    end;

    Ensuite, la ThreadList, tu peux la lire dans un Timer, par exemple, ... cela t'évitera tout problème d'utilisation d'objet comme TMemo ...
    Perso, je consulte cela durant le OnIdle, à ce moment, on sait que l'appli glandouille, autant faire les traitements qui attendent !

    tu peux aussi utiliser Synchronize, son défaut est de bloquer le thread, dans ton utilisation, cela n'aura que peu d'importance !

    Voici, un exemple, tu notes que c'est bien plus court mais bien plus moche !
    Si quelqu'un connait une méthode propre pour un Synchronize avec paramètre qui n'implique pas un page de ASM de Sjrd

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    procedure TForm1.HTTPPingEventHandler(Sender: THTTPPing; Success: boolean);
    begin
      self.URL := Sender.URL;
      self.Success := Success;   
      Synchronize(AfficheHttpPing);
    end;
     
    procedure TForm1.AfficheHttpPing();
    begin
      Memo.Lines.Add(self.URL + ' : ' + BoolToStr(self.Success, true));
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  3. #3
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut
    Merci Shai' pour toutes ces belles lignes
    A la première lecture je suis un peu perdu, mais les explications sont très utiles.

    Je me lance !

    Sinon, à quelle moment vient l'utilisation du SpinEdit pour définir le nombre de threads à lancer ?
    Je vais trop vite peut-être ?

    Beny

  4. #4
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    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 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Pour le SpinEdit,
    A toi gérer un Pool de Thread !

    Tu as pleins de solution !

    Tu gère les thread via un autre thread qui attend qu'il y en a qui finisse pour en relancer un autre, c'est très vilain car c'est ce que l'on appel de l'attente active !

    l'autre solution, c'est de passer URL: string en URLs: TStrings
    Tient le sujet Découper une StringList en plusieurs StringList ? va te servir !


    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
     
    constructor THTTPPing.Create(const URLs: TStrings; AEvent: THTTPPingEvent);
    begin
      inherited Create(false);
     
      FreeOnTerminate := true;
      FEvent := AEvent; // membre privé
      FUrls.Assign(URLs); // Copie, donc tranquille !
    end;
     
    procedure THTTPPing.Execute(); // override;
    var
      Success: boolean;
    begin
      for i := 0 to FUrls.Count - 1 do
      begin
     
        http := TIdHTTP.Create;
        http.HandleRedirects := True;
        http.ReadTimeout := 20000;
        http.ConnectTimeout := 20000;
     
        Sucess := false; 
        try
          Sucess := AnsiContainsStr(http.Get(FUrls[i]), 'blablabla');
        except
        end;
     
        http.free(); // ne pas oublier !!!
     
        if Assigned(FEvent) then
         FEvent(this, FUrls[i], Sucess); // ici, voir comment tu vas synchroniser !
      end;
    end;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    THTTPPingEvent = procedure(Sender: THTTPPing; const URL: String; Success: boolean);
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    procedure TForm1.HTTPPingEventHandler(Sender: THTTPPing; const URL: String; Success: boolean);
    begin
      self.URL := URL;
      self.Success := Success;   
      Synchronize(AfficheHttpPing);
    end;
     
    procedure TForm1.AfficheHttpPing();
    begin
      Memo.Lines.Add(self.URL + ' : ' + BoolToStr(self.Success, true));
    end;
    le code d'appel sera
    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
     
    procedure TForm1.Button1...
    ...
    begin
      MoveCount :=  ListBox1.Items.Count div SpinEdit1.Value;
      Position := 0;   
      RemainingCount := ListBox1.Items.Count;
      for k := 1 to SpinEdit1.Value - 1 do
      begin
        Tmp := TStringList.Create();
        for i := Position to RemainingCount - Position +1 do 
          Tmp.Add(ListBox1.Items[i]);
     
        THTTPPing.Create(Tmp, HTTPPingEventHandler);
        Inc(Position, MoveCount);
        Dec(RemainingCount, MoveCount);      
     
        Tmp.Free();
      end;
     
      if RemainingCount > 0 then
      begin
        Tmp := TStringList.Create();
        for i := Position to RemainingCount - Position +1 do 
          Tmp.Add(ListBox1.Items[i]);
     
        THTTPPing.Create(Tmp, HTTPPingEventHandler);
     
        Tmp.Free();
      end;
     
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  5. #5
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut
    Oui, justement mon précédent thread (euh, disons topic...) était destiné à appréhender le multithread.
    Bon tout cela me pique encore les yeux, mais j'avance, j'avance.

    Merci 1000x

    Beny

  6. #6
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut
    C'est dingue que ça soit si compliqué, ou bien que ça me semble si compliqué...

    Régulièrement j'utilise ce petit logiciel : http://www.mylanviewer.com/proxy-scanner.html
    Je lance jusqu'à 1.000 threads et tout reste super stable, pas de freeze, pas de consommation de RAM excessive, ... Je suis impressioné.
    Je ne sais plus si c'est du Delphi ou du C++, mais peu importe j'imagine...

    J'ai un paquet de petits programmes qui utilisent ce genre de boucle (cité plus haut) avec Indy, des StringList, ...
    J'avais crée un premier topic sur ce sujet, mais j'avais du abandonner tellement j'étais out

    Beny

  7. #7
    Membre éclairé
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    707
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 707
    Points : 777
    Points
    777
    Par défaut
    Quelle version de Delphi ?
    Perso j'ai rarement l'occasion d'utiliser les threads (il va bien falloir s'y mettre avec la multiplication des cores), mais en ce moment je me forme à OTL (OmniThreadLibrary) et c'est bien pratique. Bon par contre il faut un petit temps d'apprentissage...

    PS: ton lien me rappelle que j'avais placé dans la partie "Contribuez" un petit outil permettant de vérifier la validité d'une liste de proxies, en multithreads. Je débutais, soyez indulgents, mais ça fonctionnait :-p

  8. #8
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut
    Citation Envoyé par GoustiFruit Voir le message
    Quelle version de Delphi ?
    Moi ? XE2.

    Citation Envoyé par GoustiFruit Voir le message
    PS: ton lien me rappelle que j'avais placé dans la partie "Contribuez" un petit outil permettant de vérifier la validité d'une liste de proxies, en multithreads. Je débutais, soyez indulgents, mais ça fonctionnait :-p
    Ouiii ! On en avait parlé justement de ton exemple dans un précédent topic.
    Ta gestion des threads fonctionne très bien.
    Bon alors après peut-être que le code est optimisable, faudra qu'un expert y jette un oeil (à mon niveau ça me dépasse).

    Sinon ça a l'air pas mal OmniThreadLibrary, je découvre : http://www.thedelphigeek.com/2008/07...llo-world.html

    Beny

  9. #9
    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 445
    Points
    28 445
    Par défaut
    Alors pour ce genre de chose il faut raisonner par pool

    ton SpinEdit détermine le nombre de Thread à lancer

    chaque Thread boucle sur le traitement d'une URL

    le Thread principal gère quand à lui la liste.

    avec la classe TThread tu as donc une boucle dans la méthode Execute qui va (via Synchronise) demander une URL à traiter au Thread principal, il la traite puis donne le résultat (via Synchronise) au thread Principal.

    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
     
    procedure TMonThread.Execute;
    var
      Done: Boolean;
    begin
      Done := False;
      repeat
        Synchronise(GetURL);
        if FUrl = '' then
          Done := True
        else begin
         TraitementURL;
         Synchronise(RapportURL);
       end; 
      until Done or Terminated;
    end;
     
    procedure TMonThread.GetURL;
    begin
      FUrl := MainForm.NextURL;
    end;
     
    procedure TMonThread.TraitementURL;
    begin
      ...
      if AnsiContainsStr(s2.Text, 'blablabla') then
          FStatus := 'Correct !'
        else
         FStatus := 'Echec !';
    end;
     
    procedure TMonThread.RapportURL;
    begin
      MainForm.Output.Add(FUrl + ' : ' + FStatus);
    end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  10. #10
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut
    Bonsoir et merci Paul pour ces explications !
    Le code est plus simpliste, ça me rassure

    Mon code en est à ce stade là :
    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
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    unit Unit1;
     
    interface
     
    uses
      System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls,
      Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdHTTP, StrUtils, Vcl.Samples.Spin;
     
    type
      TForm1 = class(TForm)
        Memo1: TMemo;
        Memo2: TMemo;
        Button1: TButton;
        SpinEdit1: TSpinEdit;
        Label1: TLabel;
        Label2: TLabel;
        Button2: TButton;
        procedure Button1Click(Sender: TObject);
      private
      public
      end;
     
      TMonThread = class(TThread)
      private
      protected
        FUrl, NextURL, FStatus: String;
        Output, s2: TStringList;
        procedure Execute; override;
        procedure TraitementURL;
        procedure GetURL;
        procedure RapportURL;
      public
        constructor Create;
        destructor Destroy; override;
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
     
    constructor TMonThread.Create;
    begin
      inherited Create(True);
    end;
     
    destructor TMonThread.Destroy;
    begin
      inherited;
    end;
     
    procedure TMonThread.Execute;
    var
      Done: Boolean;
    begin
      Done := False;
      repeat
        Synchronize(GetURL);
        if FUrl = '' then
          Done := True
        else
        begin
          TraitementURL;
          Synchronize(RapportURL);
        end;
      until Done or Terminated;
    end;
     
    procedure TMonThread.GetURL;
    begin
      FUrl := NextURL;
    end;
     
    procedure TMonThread.TraitementURL;
    begin
      if AnsiContainsStr(s2.Text, 'blablabla') then
        FStatus := 'Correct !'
      else
        FStatus := 'Echec !';
    end;
     
    procedure TMonThread.RapportURL;
    begin
      Output.Add(FUrl + ' : ' + FStatus);
    end;
     
    { ============================================================================ }
     
    procedure TForm1.Button1Click(Sender: TObject);
    var
      i: integer;
      http: TIdHTTP;
      s1, s2, Output: TStringList;
    begin
      // Mystère...
    end;
     
    end.
    J'ai encore du mal à faire communiquer toutes ces procédures entre elles ou même à partir du Button1Click
    Je vais faire chauffer mon cerveau, et tout tester !

    Beny

  11. #11
    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 445
    Points
    28 445
    Par défaut
    en fait tu peux reprendre quasiment le code initial dans Execute

    mais s1 est remplacé par FUrl (une seule URL traitée à la fois et pas de boucle) qui provient de Memo1 via GetURL (il faut garder l'index de la dernière URL traitée dans Memo1)

    il me semble que s2 pourrait être un simple String

    et Output est remplacé par FStatus qui est utilisé dans RapportURL pour alimenter Memo2

    en fait tu dois sortir du Thread tous les accès VCL (Memo) pour les placer dans des méthodes invoquées via Synchronise.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  12. #12
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    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 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Citation Envoyé par benymypony Voir le message
    Bonsoir et merci Paul pour ces explications !
    Le code est plus simpliste, ça me rassure
    Simpliste, il l'est et ne l'est pas, car tu ne vois pas justement ce qui manque !
    Déjà mettre une dépendance directe MainForm dans un thread, désolé, ça s'est moche !

    Perso, je ne pratique pas Synchronize car j'ai plus souvent de la synchro entre thread secondaire et rarement avec le principal !
    Mais pour le cas présent, c'est plus simple !

    GetURL si tu veux avoir plusieurs thread peut être implémenté par un Évènement équivalent à celui que j'avais proposé :

    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
     
     
    procedure TMonThread.Execute;
    var
      Done: Boolean;
    begin
      Done := False;
      repeat
        Synchronise(GetURL);
        if FUrl = '' then
          Done := True
        else 
        begin
         FSuccess := TraitementURL(FUrl);
         Synchronise(RapportURL);
       end; 
      until Done or Terminated;
    end;
     
    function TMonThread.TraitementURL(const AURL; string): boolean;
    begin
      Result := AnsiContainsStr(AURL, 'blablabla');
    end;
     
    procedure TMonThread.GetURL;
    begin
      FUrl := '';
      if Assigned(FOnGetURL)
       FOnGetURL(this, FUrl);
    end;
     
    procedure TMonThread.RapportURL;
    begin
      if Assigned(FOnRapport)
       FOnRapport(this, FUrl, FSuccess);
    end;
    FOnGetURL est un event TMonThreadGetURLEvent = procedure(Sender: TObject; out URL: String);.
    FOnRapport est un event TMonThreadRapportEvent = procedure(Sender: TObject; const URL: String; Success: boolean);.

    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
    procedure TForm1.MonThreadGetURLEventHandler(Sender: TObject; out URL: String);
    begin
      if FIndex < ListBox1.Count then // FIndex  est un membre privé de TForm1
      begin
        URL := ListBox1.Items[FIndex];
        Inc(FIndex );
      end
      else
        URL := '';
    end;
     
    procedure TForm1.MonThreadGetURLEventHandler(Sender: TObject; const URL: String; Success: boolean);
    begin
      Memo1.Lines.Add(URL + ' : ' + BoolToStr(Success, true));
    end;
    L'appel est extrément simple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    procedure TForm1.Button1...
    ...
    begin
      FIndex := 0;
      for k := 1 to SpinEdit1.Value do
        TMonThread.Create(MonThreadGetURLEventHandler);
     
    end;
    En mettant FreeOnTerminate, on ne se préoccupe pas de la libération !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    constructor THTTPPing.Create(AGetUrl: TMonThreadGetURLEvent; ARapport: TMonThreadRapportEvent);
    begin
      inherited Create(false);
     
      FreeOnTerminate := true;
      FOnGetURL := AGetUrl; // membre privé
      FOnRapport := ARapport;
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  13. #13
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut
    Bonjour à vous deux, et merci pour ces explications.

    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
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    unit Unit1;
     
    interface
     
    uses
      System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls,
      Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdHTTP, StrUtils, Vcl.Samples.Spin;
     
    type
      TForm1 = class(TForm)
        Memo1: TMemo;
        Memo2: TMemo;
        Button1: TButton;
        SpinEdit1: TSpinEdit;
        Label1: TLabel;
        Label2: TLabel;
        Button2: TButton;
        procedure Button1Click(Sender: TObject);
        procedure MonThreadGetURLEventHandler(Sender: TObject; out URL: String);
      private
        Findex: Integer;
      public
      end;
     
      TMonThreadGetURLEvent = procedure(Sender: TObject; out URL: String);
      TMonThreadRapportEvent = procedure(Sender: TObject; const URL: String;
        Success: boolean);
     
      TMonThread = class(TThread)
      private
      protected
        FUrl: TStringList;
        NextURL, FStatus, Result: String;
        FSuccess: boolean;
        procedure Execute; override;
        procedure TraitementURL;
        procedure GetURL;
        procedure RapportURL;
      public
        constructor Create;
        destructor Destroy; override;
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
     
    procedure TForm1.MonThreadGetURLEventHandler(Sender: TObject; out URL: String);
    begin
      // if Findex < ListBox1.Count then
      // begin
      // URL := ListBox1.Items[Findex];
      // Inc(Findex);
      // end
      // else
      // URL := '';
    end;
     
    constructor TMonThread.Create;
    begin
      inherited Create(true);
      FreeOnTerminate := true;
    end;
     
    destructor TMonThread.Destroy;
    begin
      inherited;
    end;
     
    procedure TMonThread.GetURL;
    begin
      FUrl := '';
      if Assigned(FOnGetUrl)FOnGetUrl(this, FUrl);
    end;
     
    procedure TMonThread.Execute;
    var
      Done: boolean;
    begin
      Done := False;
      repeat
        Synchronize(GetURL);
        if FUrl = '' then
          Done := true
        else
        begin
          FSuccess := TraitementURL(FUrl);
          Synchronize(RapportURL);
        end;
      until Done or Terminated;
    end;
     
    function TMonThread.TraitementURL(const AURL; string): boolean;
    begin
      Result := AnsiContainsStr(AURL, 'blablabla');
    end;
     
    procedure TMonThread.RapportURL;
    begin
      if Assigned(FOnRapport)FOnRapport(this, FUrl, FSuccess);
    end;
     
    { ============================================================================ }
     
    procedure TForm1.Button1Click(Sender: TObject);
    var
      k: Integer;
    begin
      begin
        Findex := 0;
        for k := 1 to SpinEdit1.Value do
          TMonThread.Create(MonThreadGetURLEventHandler);
      end;
    end;
     
    end.
    J'ai encore un grand nombre d'erreurs et d'incompréhensions : concernant "FOnGetUrl" ; par quoi dois-je remplacer "ListBox1.Count" dans "MonThreadGetURLEventHandler" ; il semble manquer un "[x]" dans "TraitementURL(const AURL; string)".

    L'intégration du SpinEdit est quant à elle bien plus claire, merci !

    Une autre question bête : à quelle endroit dois-je désactiver mes contrôles pour éviter qu'ils soient utilisés pendant le process (ex : "Memo1.Enabled := False;") ?

    Merci pour votre remarquable patience !

    Beny

  14. #14
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    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 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Citation Envoyé par benymypony Voir le message
    J'ai encore un grand nombre d'erreurs et d'incompréhensions : concernant "FOnGetUrl" ; par quoi dois-je remplacer "ListBox1.Count" dans "MonThreadGetURLEventHandler" ;
    C'est évident non, la TStringList contenant les URL, un Memo1 peut-être ?
    Un peu d'effort !

    Citation Envoyé par benymypony Voir le message
    il semble manquer un "[x]" dans "TraitementURL(const AURL; string)".
    Non, il n'en manque, c'est justement l'utilisation de paramètre AURL et de Result qui évite de trop utiliser les membres privés liés au Synchronize !

    FUrl et FSuccess n'existe QUE pour le Synchronize, c'est le problème avec cette méthode de synchronisation, le passage de paramètre est une corvée !

    Demain, tu change cela, tu n'as pas à toucher TraitementURL qui se suffit à elle même !


    Ton code est incomplet, il manque des membres privés, les paramètre du constructeur, l'event Rapport et en plus tu mets Suspended à la création !

    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
     
    TMonThread = class(TThread)
      private
        FOnGetUrl: TMonThreadGetURLEvent; 
        FOnRapport: TMonThreadRapportEvent;
        FUrl: string;
        FSuccess: boolean;
        procedure TraitementURL;
        procedure GetURL;
        procedure RapportURL;
      protected
        procedure Execute; override;
      public
        constructor Create(AGetUrl: TMonThreadGetURLEvent; ARapport: TMonThreadRapportEvent; ATerminateHandler: TNotifyEvent);
        destructor Destroy; override;
      end;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    constructor TMonThread.Create(AGetUrl: TMonThreadGetURLEvent; ARapport: TMonThreadRapportEvent; ATerminateHandler: TNotifyEvent);
    begin
      inherited Create(false); // surtout pas true !
     
      OnTerminate := ATerminateHandler;
      FreeOnTerminate := true;
      FOnGetURL := AGetUrl; 
      FOnRapport := ARapport;
     
    end;

    Citation Envoyé par benymypony Voir le message
    Une autre question bête : à quelle endroit dois-je désactiver mes contrôles pour éviter qu'ils soient utilisés pendant le process (ex : "Memo1.Enabled := False;") ?
    Beny
    Ce n'est pas si bête, c'est même complexe !
    Comme les threads sont lachés dans la nature, difficile de savoir quand ils auront fini !
    Une approche basique avec un compteur et OnTerminate, tout simplement 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
    procedure TForm1.Button1Click(Sender: TObject);
    var
      k: Integer;
    begin
      Button1.Enabled := false;
      Memo1.Enabled := false;
     
      Findex := 0;
      FThreadCount := 0;
      if Memo1.Lines.Count > 0 then
      begin 
        for k := 1 to SpinEdit1.Value do
        begin
          Inc(FThreadCount);
          TMonThread.Create(MonThreadGetURLEventHandler, MonThreadRapportEventHandler, MonThreadTerminateEventHandler);
        end;
      end;
     
      if FThreadCount = 0 then
      begin
        Button1.Enabled := true;
        Memo1.Enabled := false;
      end;
    end;
    le déblocage par l’évènement OnTerminate du TThread

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    procedure TForm1.MonThreadTerminateEventHandler(Sender: TObject);
    begin
      Dec(FThreadCount);
      if FThreadCount = 0 then
      begin
        Button1.Enabled := true;
        Memo1.Enabled := false;
      end;
    end;
    J'ai oublié de renommer le nom de la fonction pour l'affichage du résultat, tu semble avoir été perturbé par cela !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    procedure TForm1.MonThreadRapportEventHandler(Sender: TObject; const URL: String; Success: boolean);
    begin
      Memo2.Lines.Add(URL + ' : ' + BoolToStr(Success, true));
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  15. #15
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    C'est évident non, la TStringList contenant les URL, un Memo1 peut-être ?
    Un peu d'effort !
    Oui désolé, je pensais que la TStringList était liée différemment...

    Mon code en est là :
    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
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    unit Unit1;
     
    interface
     
    uses
      System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls,
      Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdHTTP, StrUtils, Vcl.Samples.Spin;
     
    type
      TForm1 = class(TForm)
        Memo1: TMemo;
        Memo2: TMemo;
        Button1: TButton;
        SpinEdit1: TSpinEdit;
        Label1: TLabel;
        Label2: TLabel;
        procedure Button1Click(Sender: TObject);
        procedure MonThreadGetURLEventHandler(Sender: TObject; out URL: String);
      private
        Findex: Integer;
      public
        procedure MonThreadTerminateEventHandler(Sender: TObject);
        procedure MonThreadRapportEventHandler(Sender: TObject; const URL: String;
          Success: boolean);
      end;
     
      TMonThreadGetURLEvent = procedure(Sender: TObject; out URL: String);
      TMonThreadRapportEvent = procedure(Sender: TObject; const URL: String;
        Success: boolean);
     
      TMonThread = class(TThread)
      private
        FOnGetUrl: TMonThreadGetURLEvent;
        FOnRapport: TMonThreadRapportEvent;
        FUrl: string;
        FSuccess: boolean;
        procedure TraitementURL;
        procedure GetURL;
        procedure RapportURL;
      protected
        procedure Execute; override;
      public
        constructor Create(AGetUrl: TMonThreadGetURLEvent;
          ARapport: TMonThreadRapportEvent; ATerminateHandler: TNotifyEvent);
        destructor Destroy; override;
      end;
     
    var
      Form1: TForm1;
      Input: TStringList;
      FThreadCount: Integer;
      Result: String;
      AURL: String;
      HTTP: TIdHTTP;
     
    implementation
     
    {$R *.dfm}
     
    constructor TMonThread.Create(AGetUrl: TMonThreadGetURLEvent;
      ARapport: TMonThreadRapportEvent; ATerminateHandler: TNotifyEvent);
    begin
      inherited Create(False);
     
      OnTerminate := ATerminateHandler;
      FreeOnTerminate := true;
      FOnGetUrl := AGetUrl;
      FOnRapport := ARapport;
    end;
     
    procedure TForm1.MonThreadGetURLEventHandler(Sender: TObject; out URL: String);
    begin
      if Findex < Input.Count then
      begin
        URL := Input[Findex];
        Inc(Findex);
      end
      else
        URL := '';
    end;
     
    destructor TMonThread.Destroy;
    begin
      inherited;
    end;
     
    procedure TMonThread.GetURL;
    begin
      FUrl := '';
      if Assigned(FOnGetUrl) then
        FOnGetUrl(this, FUrl);
    end;
     
    procedure TMonThread.RapportURL;
    begin
      if Assigned(FOnRapport) then
        FOnRapport(this, FUrl, FSuccess);
    end;
     
    procedure TMonThread.Execute;
    var
      Done: boolean;
    begin
      Done := False;
      repeat
        Synchronize(GetURL);
        if FUrl = '' then
          Done := true
        else
        begin
          FSuccess := TraitementURL(FUrl);
          Synchronize(RapportURL);
        end;
      until Done or Terminated;
    end;
     
    function TMonThread.TraitementURL(const AURL: string): boolean;
    begin
      Result := AnsiContainsStr(AURL, 'blablabla');
    end;
     
    { ============================================================================ }
     
    procedure TForm1.MonThreadTerminateEventHandler(Sender: TObject);
    begin
      Dec(FThreadCount);
      if FThreadCount = 0 then
      begin
        Button1.Enabled := true;
        Memo1.Enabled := False;
      end;
    end;
     
    procedure TForm1.MonThreadRapportEventHandler(Sender: TObject;
      const URL: String; Success: boolean);
    begin
      Memo2.Lines.Add(URL + ' : ' + BoolToStr(Success, true));
    end;
     
    procedure TForm1.Button1Click(Sender: TObject);
    var
      k: Integer;
      Input: TStringList;
      HTTP: TIdHTTP;
    begin
      Button1.Enabled := False;
      Memo1.Enabled := False;
     
      HTTP := TIdHTTP.Create;
      HTTP.HandleRedirects := true;
      HTTP.RedirectMaximum := 20000;
      HTTP.ConnectTimeout := 20000;
     
      Input := TStringList.Create;
      Input.Assign(Memo1.Lines);
     
      Findex := 0;
      FThreadCount := 0;
      if Memo1.Lines.Count > 0 then
      begin
        for k := 1 to SpinEdit1.Value do
        begin
          Inc(FThreadCount);
          TMonThread.Create(MonThreadGetURLEventHandler,
            MonThreadRapportEventHandler, MonThreadTerminateEventHandler);
        end;
      end;
     
      if FThreadCount = 0 then
      begin
        Button1.Enabled := True;
        Memo1.Enabled := False;
        HTTP.Free;
      end;
    end;
     
    end.
    J'ai plein de petites erreurs, par exemple avec "FOnGetUrl(this, FUrl);" (non déclaré), ou encore "FSuccess := TraitementURL(FUrl);" (trop de paramètres originaux) et même "La déclaration de 'TraitementURL' diffère de la déclaration précédente". Wow...

    Une autre question bête : Euh... ou est passé l'utilisation du Indy IdHTTP... ?

    Merci encore !

    Beny

  16. #16
    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 445
    Points
    28 445
    Par défaut
    Tu ne comprend absolument pas ce que tu fais, du coup tu n'iras pas bien loin !

    this -> Self en Pascal
    regarde la déclaration de TraitementURL, c'est une procédure alors qu'ensuite c'est une fonction.

    @Shail, tu proposes trop de chose en même temps, c'est pas très pédagogique la référence à MainForm n'a rien de génant sauf si on veux pouvoir créer un objet Thread réutilisable, mais je crois que beny n'en est pas encore là

    on reprend donc

    1) faisons en sorte que le code ne traite qu'une seule URL sans faire référence au TMemo :
    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
     
    procedure TForm1.Button1Click(Sender: TObject);
    var
      i: integer;
      s1, Output: TStringList;
    begin
      s1 := TStringList.Create;
      Output := TStringList.Create;
     
      s1.Text := Form1.Memo1.Text;
     
    // on ajoute http:TidHTTP dans TForm1 pour le partager entre les deux méthodes
      http := TIdHTTP.Create;
      try
        http.HandleRedirects := True;
        http.ReadTimeout := 20000;
        http.ConnectTimeout := 20000;
     
        for i := 0 to s1.Count - 1 do
        begin
            Output.Add(TraitementURL(s1[i]));
        end; 
     
    // et tant qu'à faire on le détruit
      finally 
       http.Free;
      end;
     
      Form1.Memo2.Lines.Assign(Output);
     
    // manque un try/finally ...
      s1.Free;
      Output.Free;
     
      Beep;
    end;
     
    procedure TForm1.TraitementURL(const FURL: string): Boolean;
    var
      s2  : string;
    begin
      try
        s2 := http.Get(FURL);
      except
      end;
      if AnsiContainsStr(s2, 'blablabla') then
          Result := FUrl + ' : Correct !'
        else
          Result := FUrl + ' : Echec !';
    end;
    Maintenant on voudrait que ce soit fait dans N Thread,
    on va donc placer la création de TidHTTP et notre fonction Traitement URL dans une classe dérivée de TThread
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    type
      THTTPThread = class(TThread)
      private
       http: TidHTTP;
       function TraitementURL(const FUrl: string): Boolean;
      public
       procedure Execute; override;
      end;
    il reste à écrire la procédure Execute qui doit, comme expliqué dans mon premier message, demander les URL à traiter selon le code donné par Shail
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  17. #17
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    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 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Citation Envoyé par benymypony Voir le message
    J'ai plein de petites erreurs, par exemple avec "FOnGetUrl(this, FUrl);" (non déclaré),
    Oui, c'est de ma faute c'est Self au lieu de this

    Citation Envoyé par benymypony Voir le message
    ou encore "FSuccess := TraitementURL(FUrl);" (trop de paramètres originaux) et même "La déclaration de 'TraitementURL' diffère de la déclaration précédente". Wow...
    ça c'est la même erreur, là tu devrais être capable de la corriger tout seul !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    TMonThread = class(TThread)
      private
    ...
       // procedure TraitementURL; // ligne fausse
       function TraitementURL(const AURL: string): boolean; // ligne corrigée
    ...

    Citation Envoyé par benymypony Voir le message
    Une autre question bête : Euh... ou est passé l'utilisation du Indy IdHTTP... ?
    C'est TraitementURL qu'il faut remplir, il est vrai que l'on a un peu laissé de côté, déjà met en place juste le thread, qui va juste lire Momo1 et remplir le Memo2 !
    Une fois cela stable, il faudra dans le constructeur de THTTPThread, instancier un TidHTTP, l'utiliser dans TraitementURL et le libérer dans le destructeur.
    Procède étape par étape, comme c'est ton 1er thread, il est important de comprendre l'interaction TForm \ TThread avant de passer à l'étape suivante !

    Citation Envoyé par Paul TOTH Voir le message
    Tu ne comprend absolument pas ce que tu fais, du coup tu n'iras pas bien loin !
    benymypony, C'est pas faux ! Tu copies sans comprendre plus que cela, prend du recul, les petites coquilles tout de même ! on tape sur le forum un code pour toi, fait l'effort de le comprendre un peu plus !

    Citation Envoyé par Paul TOTH Voir le message
    this -> Self en Pascal
    Désolé, benymypony, je fais du C++Builder depuis plus d'un an, je suis rouillé en Delphi, c'est bien Self !
    Ce n'est pas sympa de ma part de t'embrouiller plus que tu ne l'es déjà !

    Citation Envoyé par Paul TOTH Voir le message
    @Shail, tu proposes trop de chose en même temps, c'est pas très pédagogique
    Peut-être, je me rappelle mon premier thread, j'avais beaucoup appris en étudiant le code de l'appel de OnTerminate, il est vrai que si l'on a pas accès à "classes.pas", on perd une source de connaissance énorme, ma principale source d'inspiration, et puis moi la pédagogie, c'est vrai, ce n'est pas mon fort !
    Mais bon, savoir différencier procédure et fonction, et manipuler du TNotifyEvent c'est une base à connaître avant de faire du TThread !



    Citation Envoyé par Paul TOTH Voir le message
    la référence à MainForm n'a rien de génant sauf si on veux pouvoir créer un objet Thread réutilisable, mais je crois que beny n'en est pas encore là
    Pas Faux !
    C'est pour montrer le faible couplage entre le Thread et le Form !
    Peut-être que cette indirection est un peu subtil pour un débutant !
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  18. #18
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut
    Bonsoir Shai' & Paul, merci beaucoup pour vos réponses !
    Malgré mon air perdu, j'avance grâce à vous

    Donc hier, avec un ami qui a déjà fait du multithread (mais qui n'est pas un expert non plus), nous avons essayer d'appliquer tout ce code en réalisant un simple checker de proxy.

    En gros la procédure est la suivante : connexion au proxy via Indy IdHTTP, tentative de récupérer le contenu d'une page web (méthode GET) dans une StringList, on vérifie si la StringList contient une expression bien précise à la page web demandée, si l'expression est trouvée cela signifie que le proxy permet bien l'accès à cette page et sera donc considéré comme valide.
    Oui, il y a plus simple comme méthode, mais j'ai besoin de ce processus bien précis.

    Mon interface ressemble à cela :


    Et, attention, place au code :
    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
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    unit Unit1;
     
    interface
     
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
      System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
      Vcl.StdCtrls, StrUtils, Vcl.Samples.Spin, SyncObjs, IdHTTP, IdAntiFreeze,
      IdBaseComponent, IdAntiFreezeBase, Vcl.ComCtrls;
     
    type
      TForm1 = class(TForm)
        Memo1: TMemo;
        Memo2: TMemo;
        Button1: TButton;
        SpinEdit1: TSpinEdit;
        Label1: TLabel;
        LabelValid: TLabel;
        SpinEdit2: TSpinEdit;
        Label3: TLabel;
        Memo3: TMemo;
        LabelInvalid: TLabel;
        LabelActiveThreads: TLabel;
        Button2: TButton;
        LabelList: TLabel;
        LabelRecap: TLabel;
        IdAntiFreeze1: TIdAntiFreeze;
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
        procedure Button1Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure Memo1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
        procedure Button2Click(Sender: TObject);
        procedure Memo1Change(Sender: TObject);
      private
      public
      end;
     
      TNewThread = class(TThread)
      private
        Result: Integer;
        ActiveProxy: string;
      protected
        procedure Execute; override;
      public
        procedure Sync;
        procedure LastUpdate;
        constructor Create(CreateSuspended: Boolean);
      protected
      end;
     
    var
      Form1: TForm1;
      Work: Boolean;
      cs: TCriticalSection;
      Thread, Proxies, Timeout: Integer;
      ProxiesList, InitLoad: TStringList;
      Judge, Keyword: string;
      NewThread: TNewThread;
     
    implementation
     
    {$R *.dfm}
     
    constructor TNewThread.Create(CreateSuspended: Boolean);
    begin
      inherited Create(CreateSuspended);
    end;
     
    procedure TNewThread.Execute;
    var
      CurProxy, k: Integer;
      Datas: TStringList;
      HTTP: TIdHTTP;
      ip, port: string;
    begin
      while Work do
      begin
        cs.Enter;
        Inc(Proxies);
     
        if Proxies < ProxiesList.Count then
          CurProxy := Proxies
        else
          Work := False;
     
        cs.Leave;
     
        if Work then
        begin
          Datas := TStringList.Create;
     
          ActiveProxy := ProxiesList[CurProxy];
          k := Pos(':', ActiveProxy);
          ip := Copy(ActiveProxy, 1, k - 1);
          port := Copy(ActiveProxy, k + 1, Length(ActiveProxy));
     
          HTTP := TIdHTTP.Create;
          HTTP.HTTPOptions := HTTP.HTTPOptions + [hoNoParseMetaHTTPEquiv];
          HTTP.ProxyParams.ProxyServer := ip;
          HTTP.ProxyParams.ProxyPort := StrToInt(port);
          HTTP.ReadTimeout := Timeout;
          HTTP.ConnectTimeout := Timeout;
     
          try
            // Datas.Text := HTTP.Get(Judge);
            Datas.Text := HTTP.Get('http://www.monip.org/');
          finally
            // if AnsiContainsStr(Datas.Text, Keyword) then
            if AnsiContainsStr(Datas.Text, 'IP :') then
              Result := 1
            else
              Result := 0;
          end;
     
          HTTP.Free;
          Datas.Free;
        end;
     
        Synchronize(Sync);
      end;
     
      dec(Thread);
     
      if not Work then
        Synchronize(LastUpdate);
    end;
     
    procedure TNewThread.LastUpdate;
    begin
      while not Thread = 0 do
      begin
        Form1.Button1.Enabled := True;
        Form1.Button2.Enabled := False;
        Form1.Memo1.ReadOnly := False;
        Form1.SpinEdit1.Enabled := True;
        Form1.SpinEdit2.Enabled := True;
     
        Form1.LabelActiveThreads.Caption := 'Active Threads : ' + IntToStr(Thread);
        Form1.Caption := 'HTTP Proxies Checker';
        Beep;
        Terminate;
      end;
    end;
     
    procedure TNewThread.Sync;
    begin
      case Result of
        1:
          begin
            Form1.Memo2.Lines.Add(ActiveProxy);
            Form1.LabelValid.Caption := 'Valid (' +
              IntToStr(Form1.Memo2.Lines.Count) + ')';
            Form1.LabelActiveThreads.Caption := 'Active Threads : ' +
              IntToStr(Thread);
          end;
        0:
          begin
            Form1.Memo3.Lines.Add(ActiveProxy);
            Form1.LabelInvalid.Caption := 'Invalid (' +
              IntToStr(Form1.Memo3.Lines.Count) + ')';
            Form1.LabelActiveThreads.Caption := 'Active Threads : ' +
              IntToStr(Thread);
          end;
      else
        begin
          Form1.Memo3.Lines.Add(ActiveProxy);
          Form1.LabelInvalid.Caption := 'Invalid (' +
            IntToStr(Form1.Memo3.Lines.Count) + ')';
          Form1.LabelActiveThreads.Caption := 'Active Threads : ' +
            IntToStr(Thread);
        end;
      end;
    end;
     
    { ============================================================================ }
     
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      InitLoad := TStringList.Create;
     
      if FileExists(ExtractFilePath(Application.ExeName) + 'judge.txt') then
      begin
        InitLoad.LoadFromFile(ExtractFilePath(Application.ExeName) + 'judge.txt');
        Judge := Trim(InitLoad.Text);
      end
      else
      begin
        ShowMessage('judge.txt : File cannot be found.');
        Application.Terminate;
      end;
     
      if FileExists(ExtractFilePath(Application.ExeName) + 'list.txt') then
      begin
        InitLoad.LoadFromFile(ExtractFilePath(Application.ExeName) + 'list.txt');
        Memo1.Text := InitLoad.Text;
      end;
     
      if FileExists(ExtractFilePath(Application.ExeName) + 'string.txt') then
      begin
        InitLoad.LoadFromFile(ExtractFilePath(Application.ExeName) + 'string.txt');
        Keyword := Trim(InitLoad.Text);
      end
      else
      begin
        ShowMessage('string.txt : File cannot be found.');
        Application.Terminate;
      end;
     
      LabelRecap.Caption := 'Judge : "' + Judge + '" String : "' + Keyword + '"';
      InitLoad.Free;
    end;
     
    procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
      ProxiesList.Free;
      cs.Free;
    end;
     
    procedure TForm1.Memo1Change(Sender: TObject);
    begin
      LabelList.Caption := 'List (' + IntToStr(Memo1.Lines.Count) + ')';
    end;
     
    procedure TForm1.Memo1KeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    begin
      if (Key = Ord('A')) and (ssCtrl in Shift) then
      begin
        TMemo(Sender).SelectAll;
        Key := 0;
      end;
    end;
     
    procedure TForm1.Button2Click(Sender: TObject);
    begin
      Caption := 'HTTP Proxies Checker (Waiting for threads end...)';
      Work := False;
    end;
     
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      ProxiesList := TStringList.Create;
      cs := TCriticalSection.Create;
     
      ProxiesList.Assign(Memo1.Lines);
      Timeout := (SpinEdit2.Value * 1000) div 2;
     
      Memo2.Clear;
      Memo3.Clear;
     
      Button1.Enabled := False;
      Button2.Enabled := True;
      Memo1.ReadOnly := True;
      SpinEdit1.Enabled := False;
      SpinEdit2.Enabled := False;
     
      LabelValid.Caption := 'Valid (0)';
      LabelInvalid.Caption := 'Invalid (0)';
     
      Work := True;
     
      for Thread := 1 to SpinEdit1.Value do
        TNewThread.Create(False);
     
      Thread := SpinEdit1.Value;
    end;
     
    end.
    Ça fonctionne, je pense avoir compris le principe de synchronisation, mais ça reste encore assez frais pour moi.
    Il est très fort probable que le code soit fouillis...

    J'ai constaté un problème potentiel : par exemple quand je lance 200 threads, au début tout se passe bien et puis lentement la procédure ralentit pour finalement être extrêmement lente... Comme si quelque chose se remplissait à chaque check et qu'au bout d'un moment tout est saturé. Dans le debugger je ne reçois pas de message d'alertes, pas de consommation de RAM ou CPU excessive, ... Idem quand je souhaite stopper l'opération (j'aborde ça plus bas). Peut-être est-ce dû à Indy.

    Une autre chose que j'ai du mal à comprendre, c'est la termination des threads.
    Dans mon exemple j'utilise une boolean avec While Do, ça fonctionne (sauf quand il a beaucoup de threads, comme cité plus haut) mais cela impose d'attendre la fin des threads commencés. Et comme j'utilise Indy avec un Timeout, la fin des threads prend presque le temps défini en Tiemout...
    J'ai essayé d'utiliser un AntiFreeze, mais je ne suis pas trop familier avec son utilisation.

    Avec la plupart des checker de proxies que j'utilise, lorsque je clique STOP tous les threads sont stoppés, sans temps d'attente.

    J'ai l'impression que je vais avoir encore besoin de vos lumières...
    Merci de votre patience !

    Beny

  19. #19
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut
    Bon je sens que j'ai gonflé tout le monde avec mes histoires de threads

    Je pense avoir bien compris le principe des threads et des Inc Dec qui "remplacent" la boucle for to do.

    Cependant mon problème de termination est malheureusement toujours présent.

    Beny

  20. #20
    Membre du Club
    Homme Profil pro
    Consultant en développement
    Inscrit en
    Septembre 2011
    Messages
    19
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Consultant en développement
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2011
    Messages : 19
    Points : 44
    Points
    44
    Par défaut
    Salut,

    Je commence en ce moment à mettre en pratique les threads, donc désolé si je dis quelques bêtises...


    Mes premières remarques ne concernent pas les threads mais dans ton code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    procedure TForm1.Button1Click(Sender: TObject);
    
    .../...
    
      for Thread := 1 to SpinEdit1.Value do
        TNewThread.Create(False);
     
      Thread := SpinEdit1.Value;
    end;
    Ce que j'ai mis en gras ne sert à rien si j’interprète le for du dessus


    De plus l'utilisation de la variable "Result" dans ta classe TNewThread porte à confusion, imagine que quelqu'un doive reprendre ton projet un jour et tombe dans ta procedure "TNewThread.Execute;" sur Result := 1. Ou alors qu'il veuille rajouter dans ta classe une fonction et manipuler Result.


    Sinon concernant les Threads, tu termines ta procedure Execute par :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
      if not Work then
        Synchronize(LastUpdate);

    Et le code du LastUpdate :
    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
    procedure TNewThread.LastUpdate;
    begin
      while not Thread = 0 do
      begin
        Form1.Button1.Enabled := True;
        Form1.Button2.Enabled := False;
        Form1.Memo1.ReadOnly := False;
        Form1.SpinEdit1.Enabled := True;
        Form1.SpinEdit2.Enabled := True;
     
        Form1.LabelActiveThreads.Caption := 'Active Threads : ' + IntToStr(Thread);
        Form1.Caption := 'HTTP Proxies Checker';
        Beep;
        Terminate;
      end;
    end;
    Déjà je trouve le while not Thread = 0 "tordu", c'est un avis personnel, je dois m'y reprendre à 2 fois chaque fois que je passe dessus alors que while Thread<>0 me parait plus clair... Mais bon c'est peut-être que je suis trop rouillé.

    Sinon et si j'ai bien compris ton not Thread = 0 dés qu'un thread arrive à la fin de son job il passe son temps à faire en boucle tant que y'a d'autres threads qui bossent, dans un synchronise :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        Form1.Button1.Enabled := True;
        Form1.Button2.Enabled := False;
        Form1.Memo1.ReadOnly := False;
        Form1.SpinEdit1.Enabled := True;
        Form1.SpinEdit2.Enabled := True;
     
        Form1.LabelActiveThreads.Caption := 'Active Threads : ' + IntToStr(Thread);
        Form1.Caption := 'HTTP Proxies Checker';
        Beep;
        Terminate;
    Je dis peut-être des bêtises, je me suis mis au threads depuis 3 jours, mais pour moi tu te retouves avec de plus en plus de threads qui passent leur temps à mettent à jour l'interface, spammer le beep et faire des terminate.


    Du coup je virerai le while/end.

    Je virerai aussi le terminate qui pour moi ne sert à rien ici (mais là les experts me diront si je me trompe).

    Ce qui donne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    procedure TNewThread.LastUpdate;
    begin
        Form1.Button1.Enabled := True;
        Form1.Button2.Enabled := False;
        Form1.Memo1.ReadOnly := False;
        Form1.SpinEdit1.Enabled := True;
        Form1.SpinEdit2.Enabled := True;
     
        Form1.LabelActiveThreads.Caption := 'Active Threads : ' + IntToStr(Thread);
        Form1.Caption := 'HTTP Proxies Checker';
        Beep;
    end;

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 2
    Dernier message: 14/04/2009, 15h17
  2. Jasper : première tentative
    Par Peanut dans le forum Jasper
    Réponses: 8
    Dernier message: 13/02/2009, 10h02
  3. [AJAX] [DWR] Première tentative avec Ajax
    Par bzoler dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 27/03/2008, 17h12
  4. [javamail]première tentative échouée
    Par jijaLaClasse dans le forum API standards et tierces
    Réponses: 5
    Dernier message: 22/11/2007, 20h12

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