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 :

Problèmes avec TIdFtp et l'implémentation en thread


Sujet :

Langage Delphi

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Futur Membre du Club
    Inscrit en
    Septembre 2010
    Messages
    4
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 4
    Par défaut Problèmes avec TIdFtp et l'implémentation en thread
    Bonjour à tous,

    Je voudrais vous soumettre un problème auquel je suis confronté depuis une semaine.

    Je suis en train de concevoir un programmes qui inclut un module de communication FTP.
    J'ai décidé d'utiliser le composant TIdFtp de Indy 10 (que j'ai mis à jour vers la dernière version à partir du site officiel).

    Ce composant offre la possibilité de récupérer et parser automatiquement le contenu d'un serveur via la méthode List.
    Toutefois, utiliser List dans le thread principal a le principal désavantage de bloquer le processus, ce qui est quelque peu gênant vu que certains de mes serveurs de test peuvent prendre jusqu'à 5 minutes pour répondre à LIST.

    C'est pourquoi je voulais mettre l'appel à List dans un thread séparé. Je manque de connaissance concernant les threads (c'est en fait la première fois que j'utilise des threads dans un projet majeur), et de ce fait je ne suis pas sûr que mon implémentation de List dans ce contexte soit appropriée. J'ai également tenté d'utiliser TIdAntiFreese, mais cela ne semble pas fonctionner.

    Le problème avec ce thread est que, chaque fois que je démarre le thread à partir de la procédure mère, j'obtiens une Violation d'accès (lecture à l'adresse "00000004").

    Ce qui suit est le code principal de mon thread, qui a pour seul but d'effectuer List.

    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
     
    constructor TFtpListerThread.Create(CreateSuspended: Boolean);
    begin
      inherited Create(CreateSuspended);
      FreeOnTerminate := false;
      Priority := tpNormal;
    end;
     
    procedure TFtpListerThread.DoAction;
    begin
      if (FtpTrafficBox.teststatus = 0) then     // to make sure that the thread does only one execution
        begin
          try
            FtpTrafficForm.FtpModule.List(nil, '', true);     // the most important line of this thread
          except
            FtpTrafficBox.teststatus := 2;     // teststatus evaluated to 2 in case of failure
          end;
          if (FtpTrafficBox.teststatus = 0) then
            FtpTrafficBox.teststatus := 1;     // on success, evaluated to 1
        end;
    end;
     
    procedure TFtpListerThread.Execute;
    begin
      while not Terminated do
        Synchronize(DoAction);
    end;
    Vu que le composant TIdFtp n'est pas hébergé dans ce thread, j'ai utilisé Synchronize pour éviter les problèmes d'interblocage (ou du moins, c'est supposé l'éviter).
    A ce que je sache, il n'y a aucun moyen de retourner facilement une valeur à la sortie du thread, j'ai donc utilisé une réponse par variable globale pour indiquer au processus appelant que List a été effectué. Je suis certain qu'il y a de meilleurs moyens de faire ça ; je suis ouvert à toute suggestion.

    La procédure appelante du thread principal est la suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
          teststatus := 0;     // Re-initializes the global variable
          FtpListerThread.Resume;     // Starts thread
          while (teststatus = 0) do
            begin
              sleep(10);
              Application.ProcessMessages;     // Keep process messages while it awaits for the end of the thread (not very elegant, I know)
            end;
          FtpListerThread.Suspend;     // Stops the thread when List has finished
          if (teststatus = 2) then
            begin
               // Procedures in case of failure
            end;
    Bien que le debuggueur n'ait pas été très clair sur l'origine du crash, je suis pratiquement sûr que cela se produit quand le thread tente d'effectuer List.
    De plus lorsque je tente d'éviter l'appel au thread en mettant List dans une structure try-except, cela est effectué sans aucun problème, excepté pour le problème de lag que je souhaitais éviter avec le thread.
    De nombreuses personnes ont reporté des problèmes similaires qui ont été corrigés dans une mise à jour de Indy 10 (maj qui concernait une modification d'un Integer vers un Int64 ou qq chose comme ça). Mettre à jour le Indy 10 original inclus dans Delphi n'a pas résolu le problème.

    Je pense que tout a été dit. J'espère que vous serez en mesure de me donner un coup de main dans cette histoire.
    Si vous avez besoin d'informations complémentaires, ou si vous voulez me taper sur les doigts pour mon code (dégeu ?), n'hésitez surtout pas.

    Merci d'avance pour votre aide.

    Steph.

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 937
    Par défaut
    Comme tu as fait, le thread ne sert à rien, si ce n'est introduire des erreurs
    Synchronize fait que la fonction sera de toute façon exécutée dans la tâche principale.

    Il faut plutôt créer dynamiquement le composant FTP dans OnExecute du Thread, remplir une liste propre au thread et la récupérer dans l'évenement OnTerminate.

  3. #3
    Futur Membre du Club
    Inscrit en
    Septembre 2010
    Messages
    4
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 4
    Par défaut
    Bonjour Andnotor et merci pour ta réponse.

    A vrai dire, l'idée que ce genre de thread pouvait s'avérer inutile m'a traversé l'esprit, mais le fait est que j'ai créé un autre thread qui s'occupe de se connecter au serveur sans bloquer l'application, notamment lorsque l'hôte n'existe pas.
    En testant avec et sans thread, j'ai noté que la version avec thread ne bloquait pas l'application, alors que son code est rigoureusement identique (Connect remplace List).

    S'agirait-il d'un effet placebo ?

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 937
    Par défaut
    Le thread n'est pas inutile. C'est le fait d'utiliser Synchronize qui le rend inutile

    Voilà un exemple avec création dynamique du TIdFTP et récupération du listing dans l'événement OnTerminate:

    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
    unit Unit1;
     
    interface
     
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, idFTP, StdCtrls;
     
    type
      TForm1 = class(TForm)
        Memo1: TMemo;
        procedure FormCreate(Sender: TObject);
      public
        procedure OnListComplete(Sender :TObject);
      end;
     
      TFTPListThread = class(TThread)
      private
        URL      :string;
        User     :string;
        Password :string;
        FList    :TStringList;
        FResult  :boolean;
      protected
        procedure Execute; override;
      public
        property List :TStringList read FList;
        property Result :boolean read FResult;
        Constructor Create(aURL, aUser, aPassword :string; aOnTerminate :TNotifyEvent);
        destructor Destroy; override;
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
     
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      TFTPListThread.Create('ftp.domain.com', 'user', 'password', OnListComplete);
    end;
     
    procedure TForm1.OnListComplete(Sender: TObject);
    begin
      with TFTPListThread(Sender) do
        if Result
        then Memo1.Lines.Assign(List)
        else MessageDlg('Erreur', mtError, [mbOk], 0);
    end;
     
    { TFTPListThread }
     
    constructor TFTPListThread.Create(aURL, aUser, aPassword :string; aOnTerminate :TNotifyEvent);
    begin
      inherited Create(FALSE);
     
      URL         := aURL;
      User        := aUser;
      Password    := aPassword;
      OnTerminate := aOnTerminate;
     
      FList := TStringList.Create;
      FreeOnTerminate := TRUE;
    end;
     
    destructor TFTPListThread.Destroy;
    begin
      FList.Free;
      inherited;
    end;
     
    procedure TFTPListThread.Execute;
    begin
      with TIdFtp.Create(nil) do
      begin
        try
          Username := User;
          Password := Self.Password;
          Host     := URL;
     
          Connect;
          List(FList);
     
          FResult := TRUE;
        except
          FResult := FALSE;
        end;
     
        Free;
      end;
    end;
     
    end.

  5. #5
    Futur Membre du Club
    Inscrit en
    Septembre 2010
    Messages
    4
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 4
    Par défaut
    Ok merci Andnotor, je teste tout ça et je te tiens au courant.

  6. #6
    Futur Membre du Club
    Inscrit en
    Septembre 2010
    Messages
    4
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 4
    Par défaut
    A vrai dire, entretemps, j'ai trouvé d'où venait le bug. Il s'agissait d'un problème lié à ces deux lignes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    var
      FtpListerThread: TFtpListerThread;
    Bien que je ne me souvienne pas pourquoi je les ai rajoutées (je devais être fatigué), j'avoue ne pas comprendre pourquoi elles font planter le thread.

    J'avais posé ce même problème sur le site d'Embarcadero. Pour ceux que ça intéresse, voici le lien.

    Merci encore !

Discussions similaires

  1. Réponses: 1
    Dernier message: 08/08/2006, 15h39
  2. [TidFtp] Problème avec les FTP List Parse
    Par Philbzh dans le forum Delphi
    Réponses: 1
    Dernier message: 20/06/2006, 09h48
  3. Réponses: 11
    Dernier message: 14/02/2006, 00h26
  4. Thread--> problème avec ThreadProc
    Par stof dans le forum MFC
    Réponses: 33
    Dernier message: 08/06/2005, 13h47
  5. Réponses: 5
    Dernier message: 10/05/2005, 10h22

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