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

API, COM et SDKs Delphi Discussion :

[Thread] Détruit, mais jamais 'nil'


Sujet :

API, COM et SDKs Delphi

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    6
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 6
    Par défaut [Thread] Détruit, mais jamais 'nil'
    Bonjour à tous,

    Je travaille en ce moment sur un projet sous Delphi 7. Je rentre directement dans le vif du sujet : j'utilise une classe TThApport, dérivée de la classe TThread pour exécuter un travail de calcul relativement long sans pour autant provoquer un freeze de l'interface utilisateur, ce à quoi ils sont destinés donc normalement.

    Pour décrire sommairement la structure, j'ai donc une classe TDtDeper qui contient une liste nommée LThApport de TThApport :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    (...)
    LThApport : array[1..20] of TThApport;
    (...)
    Le code de la classe TTHApport :
    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
    unit ThreadApport;
     
    interface
     
    uses
      Classes, Windows, DtPcDep, SysUtils, SyncObjs;
     
    type
      TThApport = class(TThread)
      private
        JD, JF, HD, HF : integer;
        UnJour, UneHeure : integer;
        FEvent : TEvent;
      protected
        procedure Execute; override;
        procedure DoWork;
        procedure OnTerminateProcedure(Sender : TObject);
      public
        ThPieceDep : TPieceDep;
        ThreadTerminated : boolean;
        constructor Create(CreateSuspended : boolean ; UnePieceDep : TPieceDep ; JDCall,JFCall,HDCall,HFCall : integer);
        procedure Kill;
        destructor Destroy; override;
      end;
     
    implementation
     
    { TThApport }
     
    constructor TThApport.Create(CreateSuspended : boolean ; UnePieceDep : TPieceDep ; JDCall,JFCall,HDCall,HFCall : integer);
    begin
      inherited Create(CreateSuspended);
      FreeOnTerminate := True;
      ThreadTerminated := False;
      ThPieceDep := UnePieceDep;
      FEvent := TEvent.Create(nil, False, False, '');
      JD := JDCall;
      JF := JFCall;
      HD := HDCall;
      HF := HFCall;
      UnJour := JDCall;
      UneHeure := HDCall;
      OnTerminate := OnTerminateProcedure;
    end;
     
    destructor TThApport.Destroy;
    begin
      //déchargez la mémoire ici si vous avez créé des objets
      if FEvent <> nil then FreeAndNil(FEvent);
      inherited;
    end;
     
    procedure TThApport.OnTerminateProcedure(Sender : TObject);
    var
      Tampon : string;
    begin
      ThreadTerminated := True;
    end;
     
    procedure TThApport.Kill;
    begin
      FEvent.SetEvent;
    end;
     
     
    procedure TThApport.Execute;
    var
      UnJour : integer;
      UneHeure : integer;
      //UneListeApport : TList;
      Tampon : string;
    begin
      ThPieceDep.LApportGen.Clear;
     
      while not Terminated do
      begin
        try
          case FEvent.WaitFor(10) of
            wrSignaled : Terminate;
            wrTimeout : DoWork;
          end;
        finally
        end;
      end;
     
    end;
     
    procedure TThApport.DoWork;
    var
      UnApportTotal : Double;
    begin
      {Le boulot}
     
      end;
    end;
     
    end.
    Lors d'évènements particuliers, je passe dans une structure qui gère la création d'un nouveau thread, ou encore l'arrêt / relance d'un thread selon l'élément sur lequel il travaille. Cette structure est implémentée dans une classe différente, TPiece. Pour récupérer la liste de thread, je fais en début de la fameuse structure un truc du style

    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
    (...)
      DtDeper := Owner as TDtDeper;
      (...)
      //S'il faut lancer un nouveau thread
      if NewThread then
      begin
          DtDeper.LThApport[PremierThreadLibre] := TThApport.Create(false, UnePiece, JD, JF, HD, HF);
      end
      else
      begin
        //On arrête le thread
        DtDeper.LThApport[i].Kill;
        //Et on le relance
        DtDeper.LThApport[i] := TThApport.Create(false, UnePiece, JD, JF, HD, HF);
      end;
    (...)
    L'élément en question, nommé UnePiece, contient une liste reconstruite par le thread. J'associe un thread à chaque pièce, s'arrêtant si de nouvelles modifications sont apportées sur la pièce, et se relançant.

    Jusqu'ici, rien de bien sorcier, et rien de bien intéressant ... Les soucis viennent plus tard. Mon premier, c'est qu'en debug, les threads ne se libèrent pas, alors que TrueOnTerminate est à true et qu'en mode pas à pas, ils passent bien dans le .Destroy : les éléments de la liste de thread ne sont jamais à 'nil'.
    Pire, en debug toujours, les éléments de la liste de thread contiennent des infos différentes, suivant la classe d'où j'inspecte la liste de threads (TDtDeper ou TPiece).

    Mon second souci, c'est que bien réutilisant les mêmes threads, qui ne sont jamais 'nil' puis 'Create' de nouveau, mais donc réinitialisés ... Et bien je bouffe 8 méga de ram à chaque passage dans un de mes threads. Même lorsque j'en réutilise un, je prends de nouveau 8 méga de ram.

    Donc, j'ai plusieurs pistes, et j'ai besoin de votre aide :p

    La première : je me plante dans la visibilité de ma liste de threads, je ne parle pas des mêmes lorsque je contrôle leur état dans les différentes classes. Du coup, ma gestion est foireuse.

    La deuxième : si je détruis 'ThPieceDep' dans mon destroy de thread, je le détruis aussi dans ma liste de piece dans DtDeper, je ne peux donc pas le faire, mais est-ce que 'ThPieceDep' qui n'est donc pas détruit, n'empêche pas le destroy du thread de s'accomplir ? A ce moment là, dois-je travailler avec un pointeur pour ma fameuse pièce ?

    Que de mots mal employés, je me rends compte que le problème exposé n'est pas très clair (moitié thread, moitié visibilité de variable), mais je suis un peu à cours de ressources, ayant parcouru un certain nombre de forums sur le sujet des threads. Bien que ce soit mon premier post, ça fait un moment que je parcours ce forum, je compte sur vous, et si j'ai mal expliqué un certain nombre de points, n'hésitez pas !

    Merci d'avance, Risk.

  2. #2
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 086
    Par défaut
    C'est à toi de mettre à nil (dans OnTerminateProcedure, qui se produit via un Synchronize ! donc tu peux accéder au tableau des Thread, tu passe par exemple sa position dans le tableau au create pour mettre à nil cette case au destroy, d'ailleurs, le destroy pourrait le faire lui même), ça ne se fait pas tout seul, ne pas confondre le Free (Destroy) et la mise à nil, sinon il n'y aurait pas une fonction FreeAndNil ...

    Ensuite, pour ta pièce, doit-elle être utilisé par l'arret du thread, si oui, il ne faut pas la libérer ... tu devrais reposer le tout à plat c'est confu !
    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 à l'essai
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    6
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 6
    Par défaut
    Merci pour cette réponse rapide.

    En toute logique, en mettant à nil la case contenant le thread, je libérerais donc la mémoire allouée, Ok.

    Pour la pièce, il s'agit donc d'un objet que doit venir compléter le thread, mais ce qui me trouble, c'est : est-ce que le thread travaille sur une copie de la pièce, ou travaille-t-il sur la seule et unique pièce faisant partie de la liste de pièce ? Je penche pour la deuxième hypothèse, puisqu'en mettant UnePiece.Free dans TThApport.Destroy, ma pièce n'est plus visible par la suite dans mon application.

    Avec ce postulat, je ne peux donc pas libérer mon thread puisqu'il "partage" une instance avec le reste de mon appli. Je me trompe ?

  4. #4
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 086
    Par défaut
    Citation Envoyé par Riskmaker Voir le message
    En toute logique, en mettant à nil la case contenant le thread, je libérerais donc la mémoire allouée, Ok.
    euh, non, le Free libère la mémoire allouée (c'est "FreeOnTerminate à True" qui le fait), le nil ne fait que mettre 00 00 00 00 comme adresse de pointeur, ...

    le thread travaille sur la même instance, en delphi, tout objet est sous la forme de pointeur (c'est comme l'allocation dynamique du c++, l'allocation statique n'existe pas en delphi, il n'y a jamais de copie d'objet)

    Citation Envoyé par Riskmaker Voir le message
    Avec ce postulat, je ne peux donc pas libérer mon thread puisqu'il "partage" une instance avec le reste de mon appli. Je me trompe ?
    Ben tout dépend la durée de vie de l'objet Pièce et du Thread, ... mais normalement, l'objet Piece doit être libéré par le thread qui l'a créée, le thread ne devrait pas y toucher ... sauf si la Piece n'est créé que pour être dans le thread, et dans ce cas, tu ne devrais même pas conservé l'instance de la pièce hors du thread ... c'est pour cela que je disais de tout mettre à plat, de faire des petits dessins pour que tu visualise la piece, le thread principal (contenant la fenêtre princiapale) et le thread secondaire
    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 à l'essai
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    6
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 6
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    euh, non, le Free libère la mémoire allouée (c'est "FreeOnTerminate à True" qui le fait), le nil ne fait que mettre 00 00 00 00 comme adresse de pointeur, ...
    Ok, je vois la nuance. Dans ce cas là, je ne comprends pas. En utilisant simplement le gestionnaire des tâches de windows, je constate qu'environ 8 mégas de ram sont consommés à chaque thread lancés. En toute logique, avec FreeOnTerminate := True dans le constructeur, je devrais récupérer ces 8 mégas ?

    Citation Envoyé par ShaiLeTroll Voir le message
    Ben tout dépend la durée de vie de l'objet Pièce et du Thread, ... mais normalement, l'objet Piece doit être libéré par le thread qui l'a créée, le thread ne devrait pas y toucher ... sauf si la Piece n'est créé que pour être dans le thread, et dans ce cas, tu ne devrais même pas conservé l'instance de la pièce hors du thread ... c'est pour cela que je disais de tout mettre à plat, de faire des petits dessins pour que tu visualise la piece, le thread principal (contenant la fenêtre princiapale) et le thread secondaire
    Pour clarifier, la pièce doit exister du lancement de l'appli à l'arrêt. Le thread secondaire n'a que pour but de remplir un TStringList membre de cette pièce, lors d'une modificiation de la dite-pièce, c'est pour cela que je la passe dans le Create du TThApport.

    Il n'est appelé que ponctuellement : je prends bien soin de faire un Clear sur la TStringList de la pièce lorsque je relance un thread sur la pièce, puis, je la reremplit. Je ne comprends pas d'où vient cette fuite mémoire, suivant ce que tu as écrit plus haut, puisque je travaille en permanence sur la même instance de pièce.

  6. #6
    Membre à l'essai
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    6
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 6
    Par défaut
    Pour continuer un ptit peu, je suis passé sur plusieurs instances du thread en mode pas à pas. J'ai constaté en utilisant @UnePiece que la valeur retournée variait à chaque instance, alors que je travaille sur la même pièce, jamais détruite.

    Question de noob (que je suis, si vous l'aviez pas remarqué :p) : la valeur retournée correspond à l'adresse du pointeur, ou celle de l'objet ? Si c'est la 2ème option, ma fuite de mémoire viendrait donc du fait que j'utilise une nouvelle pièce à chaque instanciation du thread de calcul ?... Si c'est la première, je capte pas :p

    Edit : je me réponds moi-même : ftp://ftp-developpez.com/delphi/cour...urs-delphi.pdf page 27 ...

    La deuxième hypothèse est donc la "bonne", mais c'est en contradiction avec ce que tu as indiqué dans ton dernier post, Shai. J'ai mal interprété quelque chose ?

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 0
    Dernier message: 19/03/2013, 16h33
  2. "ValidationRule" instanciée mais jamais invoquée
    Par Pragmateek dans le forum Windows Presentation Foundation
    Réponses: 4
    Dernier message: 15/06/2009, 16h18
  3. [SimpleXML] Noeud fermé mais jamais ouvert : impossible à analyser
    Par glopglopyoup dans le forum Bibliothèques et frameworks
    Réponses: 2
    Dernier message: 26/11/2007, 23h54
  4. La variable 'j' est déclarée mais jamais utilisée?
    Par Bruno13 dans le forum Langage
    Réponses: 8
    Dernier message: 17/10/2007, 08h56
  5. [C# 2.0]Thread externe mais bloquant main interface
    Par Psykotik dans le forum Windows Forms
    Réponses: 13
    Dernier message: 31/08/2007, 09h59

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