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 :

[Delphi XE7] Echange de données entre thread et fiche


Sujet :

Delphi

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Candidat au Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2018
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2018
    Messages : 2
    Par défaut [Delphi XE7] Echange de données entre thread et fiche
    Bonjour à tous,

    je suis confronté à un problème qui me semble être un "DeadLock".

    je suis en train de développer une application dont une des fonctionnalités est d'afficher en temps réelles des données récupérées sur des équipements distants (en Tcp).
    Pour ce faire, j'ai deux threads:
    • Thread de Dialogue TCP
    • Thread Dispatch Données TCP


    Thread Dialog TCP
    Son rôle est d'aller chercher cycliquement les données des équipements distant via TCP et de les stocker d'une façon structuré dans une mémoire partagée.
    Ce Thread ne pose pas de problème (pour le moment)

    Thread Dispatch Données TCP
    Ce thread récupère les données de la mémoire partagée du thread de dialogue est les envoie aux fiches ayant besoin de celle-ci.
    Pour ce faire, je suis parti sur un principe "d'abonnement" aux données.
    A l'ouverture d'une fiche de l'application qui consomme ces données, elle s'abonne aux données en appelant la procédure "EventOnDispatchAdd" en lui passant comme paramètre la procédure de la fiche qui doit être appelé pour afficher les données. Le thread Stock la liste des procédures de fiche dans une variable TList servant comme liste de diffusion.
    A la fermeture de cette fiche, elle se désabonne en appelant la procédure "EventOnDispatchDel" qui supprime la procédure de la fiche de la liste de diffusion.
    C'est dans ce principe que régulièrement, voir assez souvent que mon application se fige.
    Pour éviter les accès simultanées à la liste de diffusion du Thread, j'ai encadré l'utilisation de celle-ci par une section critique.
    Mon avis sur les sections critiques est qu'elles peuvent être assimilées comme des mutex mais pour un processus. Est-ce bien cela ?

    En tous cas, il me semble que mon application se fige bien sur une "DeadLock" du fait de l'utilisation des sections critiques et conjointement de l'instruction Synchronize du Thread.

    En espérant avoir été assez claire dans ma description.
    Merci d'avance pour votre aide.

    ----------------------------------------------------------------------------------
    Un extrait du code posant problème vaut mieux qu'un long discours.

    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
     
    {
    Extrait de la définition, constructeur et destructeur du thread Dispatch
    }
     
    type
      TNotifyOnDispatchTcp = procedure(aPData: PTcpMsgSrvOutData) of object;
     
     type
      PThDispatchTcp = ^TThDispatchTcp;
      TThDispatchTcp = class(TThread)
      private
        fLock           : TRTLCriticalSection;
        fLstOnDispatch  : TList<TNotifyOnDispatchTcp>;
        procedure Cycle;
      protected
        procedure Execute; override;
      public
        procedure EventOnDispatchAdd(aProc : TNotifyOnDispatchTcp);
        procedure EventOnDispatchDel(aProc : TNotifyOnDispatchTcp);
        // ------------------------------
        constructor Create;
        destructor Destroy; override;
    end;
    // ------------------------------------------------------
    constructor TThDispatchTcp.Create;
    begin
      try
        fLstOnDispatch := TList<TNotifyOnDispatchTcp>.Create;
        InitializeCriticalSection(fLock);
      except
        on E: Exception do
          LogTrace(SHRMEMDATALOG_LOG_ERROR,E.Message);
      end;
    end;
    // ------------------------------------------------------
    destructor TThDispatchTcp.Destroy;
    begin
      fLstOnDispatch.Free;
      DeleteCriticalSection(fLock);
      inherited;
    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
    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
     
    {
    Extrait des procédures qui a mon sens pose problème
    procédure Cycle => Diffusion des données (appel cyclique par API CreateWaitableTimer)
    procédure EventOnDispatchAdd => Ajout d'un abonnement
    procédure EventOnDispatchDel => Supprime un abonnement
    }
    procedure TThDispatchTcp.Cycle;
    var
      I : Integer;
    begin
      try
        // Récupère les données Tcp de la mémoire
        fTcpMemData.ReadData(@fMsg);
     
        // Diffuse les données 
        EnterCriticalSection(fLock);
        try
          for I := 0 to fLstOnDispatch.Count - 1 do
            Synchronize
                (
                Self,
                procedure
                begin
                  fLstOnDispatch[I](@fMsg);
                end
                );
        finally
          LeaveCriticalSection(fLock);
        end;
     
      except
        on E : Exception do
          LogTrace(SHRMEMDATALOG_LOG_ERROR,E.Message);
      end;
     
    end;
    // ----------------------------------------------
    procedure TThDispatchTcp.EventOnDispatchAdd(aProc : TNotifyOnDispatchTcp);
    begin
      try
        EnterCriticalSection(fLock);
        try
          if fLstOnRefresh.IndexOf(aProc) = -1 then
            fLstOnRefresh.Add(aProc);
        finally
          LeaveCriticalSection(fLock);
        end;
     
      except
        on E: Exception do
          LogTrace(SHRMEMDATALOG_LOG_ERROR,E.Message);
      end;
     
    end;
    // ----------------------------------------------
    procedure TThDispatchTcp.EventOnDispatchDel(aProc : TNotifyOnDispatchTcp);
    begin
      try
        EnterCriticalSection(fLock);
        try
          fLstOnDispatch.Remove(aProc);
        finally
          LeaveCriticalSection(fLock);
        end;
     
      except
        on E: Exception do
          LogTrace(SHRMEMDATALOG_LOG_ERROR,E.Message);
      end;
     
    end;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    {
     Code a l'ouverture d'un fiche par exemple
    }
        thDispatchTcp.EventOnDispatchAdd(Self.RefreshValue);

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 935
    Par défaut
    Il y a à mon sens une erreur de conception. La boucle dans Cycle devrait être incluse dans Synchronize. Il n'y aurait ainsi plus besoin de section critique.

    La section critique serait surtout adaptée pour protéger la lecture/écriture de FMsg si tu voulais faire du 100% asynchrone.

  3. #3
    Candidat au Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2018
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2018
    Messages : 2
    Par défaut
    Bonjour,

    Je viens d'appliquer vos recommandation : Ajout de la boucle dans le Synchronize et suppression de la section critique.
    Tous fonctionne pour le moment.
    Merci beaucoup.

    Les accès concurrent avec les threads après les fuites mémoires ont toujours été mes craintes.
    Peut être que j'abuse des sections critiques, mutex ... surement parce que les mécanismes internes liés aux threads dont la fonction synchronize me sont encore obscure.

    En tous cas merci beaucoup.

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

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

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Par défaut
    C'est pourtant pas si compliqué.

    Synchronize suspend l'exécution du thread et place la procédure donnée en paramètre en attente d'exécution. Quand le thread principal ne fait rien, il en profite pour traiter les Synchronize en attente... Donc dans ce cas de figure, les sections critiques ne sont pas nécessaires car la procédure ne peut pas être en conflit avec le code "normal" du thread principal qui est lui mono-tâche.

    Par contre, si le thread veut accéder à une liste qui est aussi manipulée par un autre thread (principal ou pas), là il faut un mécanisme de partage comme la section critique qui au contraire du mutex n'est valide qu'à l'intérieur d'un même process (un process pouvant avoir plusieurs threads). Pour synchroniser plusieurs process (plusieurs instances du même exécutable par exemple) il faut un mutex... mais entre plusieurs process il n'y a pas de partage de mémoire et donc d'objets.

    Il m'arrive souvent d'avoir une liste dans le thread principal avec un thread qui utilise Synchronize pour en extraire ou ajouter un élément... Donc là pas de section critique vu que la liste est manipulée exclusivement par le thread principal.

    Autres éléments de synchronisation pratiques sous Windows : SendMessage() et PostMessage(). Le premier est bloquant : le thread est donc suspendu tant que le message n'est pas traité par la fenêtre destinatrice. Le second permet juste d'envoyer une info (comme la mise à jour d'une progressbar par exemple)... Attention dans ce cas aux paramètres du message, si on y place un pointeur vers une donnée, celle-ci doit être toujours disponible quand le message sera traité.

    Mais on peut faire des trucs sioux comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    procedure TMonThread.Execute;
    var
      sl: TStringList;
    begin
      sl := TStringList.Create;
      ...
      PostMessage(MainForm.Handle, WM_USER, 0, Integer(sl)); // on peut très bien envoyer un objet :)
    end;
     
    procedure TMainForm.WMUser(var Msg: TMessage); // message WM_USER
    var
      sl: TStringList;
    begin
      sl := TStringList(Msg.lParam);
      ...
      sl.Free; // c'est ici qu'on le libère
    end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

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

Discussions similaires

  1. [Tableaux] Echange de données entre JSP et PHP
    Par seb34 dans le forum Langage
    Réponses: 4
    Dernier message: 21/02/2006, 13h53
  2. Echange de données entre JSP et PHP
    Par seb34 dans le forum Servlets/JSP
    Réponses: 2
    Dernier message: 21/02/2006, 13h28
  3. Réponses: 16
    Dernier message: 29/06/2005, 20h55
  4. Telechargement d'internet et echange de données entre 2 pc
    Par Invité dans le forum Développement
    Réponses: 5
    Dernier message: 09/05/2004, 21h22
  5. [Kylix] Echange de données entre fiches
    Par _dack_ dans le forum EDI
    Réponses: 1
    Dernier message: 01/07/2003, 11h34

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