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 :

[Thread] Partage d'objet


Sujet :

Langage Delphi

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé Avatar de femtosa
    Inscrit en
    Juin 2002
    Messages
    253
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 253
    Par défaut [Thread] Partage d'objet
    Hello

    Voici mon soucis : j'ai un thread qui reçoit des données TCP ( sjrd et Clorish !) dans un buffer (de type array of Bytes).

    Ce buffer, je le passe depuis le thread principale. Seulement, j'ai des violation d'accès là-dedans ... !

    Le principe que j'ai mis en place est le suivant : J'ai un tableau privé dans mon thread. Je renseigne cette variable par le biais d'une méthode public. Une fois renseignée, je dis au thread d'effectuer la lecture TCP, puis une fois fait, je traite le tableau dans mon thread principal (qui aura été modifié par mon thread).

    Voici les deux solutions que j'ai essayé pour renseigné mon champ privée de mon thread de lecture :

    1. Passage d'adresse par pointeur
    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
    type
      TBytes = array of Bytes;
      PTBytes = ^TBytes;
     
    //Dans mon thread principale
    procedure TWTQConnection.GetResponse(var AResponse: array of Byte); //AResponse est la tableau où je souhaite récupérer la réponse TCP
    var
      ReadBuffer: TBytes; //Utilisé pour passé au thread
      i: Integer;
    begin
      SetLength(ReadBuffer, TRAME_LENGTH); //Initialisation
      TCPThread.SetResponseTab(@ReadBuffer); //Renseignement du champ du thread
      TCPThread.DoRead; //Dis au thread de faire la lecture TCP
      repeat
        Sleep(10);
        Beep; //Fait quelque chose pendant que le thread est bloqué sur la lecture de la réponse
      until TCPThread.GetResponse;
      for i := D9_BYTE to D0_BYTE do
        AResponse[i-D9_BYTE] := ReadBuffer[i]; //Copie de la réponse dans le tableau reçu en paramètre : ACCESS VIOLATION ... !!
    end;
     
    //Dans mon thread
    procedure TTCPThread.Execute;
    begin
      while(not Self.Terminated) do
      begin
        if(ReadResponse) then
        begin
          TCPClient.ReceiveBuf(Response^, TRAME_LENGTH);  //Lecture TCP
          ReadResponse := false;                 //Mise à jour de variable d'état
          HasResponse := true;
        end;
        Sleep(1);
      end;
    end;
     
    procedure TTCPThread.SetResponseTab(AResponse: PTBytes);
    begin
      Response := AResponse;               //Renseignement du champ privé
    end;
     
    procedure TTCPThread.DoRead;
    begin
      ReadResponse := true;                //Dis au thread de faire une lecture
    end;
     
    function TTCPThread.GetResponse;
    begin
      GetResponse := HasResponse;    //Pour savoir si le thread à effectué sa lecture
    end;
    2. Passage de variable par référence

    (solution identique à la précédente, sauf qu'au lieu de travailler avec des pointeur, j'ai utilisé des TBytes comme type partout, avec des 'var' dans les procédures).


    Dans la première solution, j'ai un Access Violation à la ligne en rouge. Dans la deuxième solution, j'ai un Access Violation lors que je renseigne mon champ privé pour la deuxième fois (pour la première fois, tout fonctionne).

    J'espère avoir expliqué la chose suffisamment clairement pour ne pas vous dégouter !!

    Merci d'avance pour votre aide et commentaires !

  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
    Bon, le pointeur sur tableau dynamiques, faut avoir du courage ... moi, j'ai tenté l'expérience, j'ai échoué (vu que le tableau dynamique c'est en fait un pointeur sur un tableau statique réalloué à chaque changement de taille) ... je fais préfère un type fait main
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    type  
      PBidule = ^TBidule;
      TBidule = record
         ...
      end;  
    TBiduleArray = Record
      ArrayLength: Integer;
      ArrayPointer: PBidule;
      end;
    Bon si TBidule est un type Byte (Voir Types.TByteDynArray), ce la fonctionne très bien

    et j'alloue avec GetMem/FreeMem, un pointer de la taille
    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
     
    var
      BiduleArray: TBiduleArray;
      CurrentBidule: PBidule;
      I: Integer;
    begin
      BiduleArray.ArrayLength := TRAME_LENGTH * SizeOf(TBidule)
      GetMem(BiduleArray.ArrayPointer, BiduleArray.ArrayLength);
     
      CurrentBidule := BiduleArray.ArrayPointer;
      for i := 0 to BiduleArray.ArrayLength - 1 do
      begin
        CurrentBidule^.Machin := Truc;
     
        Inc(CurrentBidule);
      end;
    il existe une autre façon de le déclarer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    type  
      TBidule = record
         ...
      end;  
    PBiduleItems = ^TBiduleItems;
    TBiduleItems = Array[0..MaxInt div 16] of TBidule; // bon 16 c'est pour l'exemple, il faut mettre la taille réelle de TBidule ou le nombre maximal connu pour le programme directement
    TBiduleArray = Record
      ArrayLength: Integer;
      ArrayPointer: PBiduleItems;
    end;
    pour jouer avec ça ne change pas grand chose quoi que ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    var
      BiduleArray: TBiduleArray;
      I: Integer;
    begin
      BiduleArray.ArrayLength := TRAME_LENGTH * SizeOf(TBidule);
      GetMem(BiduleArray.ArrayPointer, BiduleArray.ArrayLength);
     
      for i := 0 to BiduleArray.ArrayLength - 1 do
      begin
        BiduleArray.ArrayPointer^[I].Machin := Truc;
      end;
    En fait, c'était comme cela que je faisais des tableaux dynamiques en delphi 3 car ceci ont été intégré quand delphi 4 ! On utilise un pointeur sur un tableau fixe (ce qui ressemble ni plus ni moins à ce que je disais plus haut à un tableau dynamique)

    Ainsi j'ai un pointeur, et j'ai le nombre d'item sous la main, et j'ai en plus toute maitrise sur la mémoire ...

    Sinon, je passe effectivement, mon tableau en paramètre var et je n'ai aucun soucis ... et cela m'évite tout ce code ...

    Attaquons nous au Thread, tu pourrais remplacer ton ReadResponse par un Switch Resume/Suspend, cela économiserais du processeur ? non ?

    Il me semble que ReceiveBuf renvoie le nombre d'octet reçu, il sera justifié de l'utiliser !
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ReadResponse := TCPClient.ReceiveBuf(Response^, TRAME_LENGTH) > 0;
    Ensuite, le i-D9_BYTE, m'étonne, tu inverse le contenu du tableau ? ça devrait être D9_BYTE-i non ?
    D9_BYTE vaut combien ? est-il supérieur à D0_BYTE, dans ce cas un downto au lieu de to ?

    j'aurais écrit
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
      if Length(AResponse) >= Length(ReadBuffer) then 
        for i := Low(AResponse) to High(AResponse) do
          AResponse[i] := ReadBuffer[i + Low(ReadBuffer)]; 
      else
        raise EKaBoom.Create('@&#]^!%¤');
    ou j'aurais écrit

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    if Length(AResponse) >= Length(ReadBuffer) then 
      CopyMemory(@AResponse[Low(AResponse)], @ReadBuffer[Low(ReadBuffer)], Length(AResponse));
    else
        raise EKaBoom.Create('@&#]^!%¤');
    Mais, j'ai l'impression que tu inverse le tableau ... non ?
    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 éclairé Avatar de femtosa
    Inscrit en
    Juin 2002
    Messages
    253
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 253
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Bon, le pointeur sur tableau dynamiques, faut avoir du courage ...
    J'ai utilisé les pointeurs car j'en ai bien l'habitude en C et que c'était faisable facilement dans ce langage ... Maintenant, en Delphi c'est autre chose apparemment ... ! En fait, mes tableaux sont de tailles fixes. J'ai utilisé des tableaux dynamiques car dans l'en-tête d'une fonction, c'est plus simple de le déclarer dynamique le tableau n'est-ce pas ?

    Citation Envoyé par ShaiLeTroll Voir le message
    ... je fais préfère un type fait main <...>
    et j'alloue avec GetMem/FreeMem, un pointer de la taille
    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
    var
      BiduleArray: TBiduleArray;
      CurrentBidule: PBidule;
      I: Integer;
    begin
      BiduleArray.ArrayLength := TRAME_LENGTH * SizeOf(TBidule)
      GetMem(BiduleArray.ArrayPointer, BiduleArray.ArrayLength);
     
      CurrentBidule := BiduleArray.ArrayPointer;
      for i := 0 to BiduleArray.ArrayLength - 1 do
      begin
        CurrentBidule^.Machin := Truc;
     
        Inc(CurrentBidule);
      end;
    Ne devrait-on pas remplacer 'BiduleArray.Length - 1' par 'TRAME_LENGTH' ? J'interprète le champs 'ArrayLength' comme la taille du tableau en byte et non en élément, je me trompe ? Le nombre d'élément étant défini par 'TRAME_LENGTH' ...

    Citation Envoyé par ShaiLeTroll Voir le message
    En fait, c'était comme cela que je faisais des tableaux dynamiques en delphi 3 car ceci ont été intégré quand delphi 4 ! On utilise un pointeur sur un tableau fixe (ce qui ressemble ni plus ni moins à ce que je disais plus haut à un tableau dynamique)

    Ainsi j'ai un pointeur, et j'ai le nombre d'item sous la main, et j'ai en plus toute maitrise sur la mémoire ...

    Sinon, je passe effectivement, mon tableau en paramètre var et je n'ai aucun soucis ... et cela m'évite tout ce code ...
    Alors aujourd'hui travailles-tu comme tu me l'as expliqué ? Je comprends le code que tu m'as fourni, mais en fait, est-ce que cela corrigerai mon problème ? Car le principe de gérer moi-même tableau dynamique est clair et je te remercie de tout ce code ! Mais lorsque je devrait passé ce record en paramètre de fonction, c'est juste passé un 'TBiduleArray' en paramètre ? Et je récupère tout dans le thread et je peux le modifier ?
    Je t'avoue que si je pouvais travailler simplement avec des tableaux et des paramètres déclaré var, ça m'arrangerai ...

    Citation Envoyé par ShaiLeTroll Voir le message
    Attaquons nous au Thread, tu pourrais remplacer ton ReadResponse par un Switch Resume/Suspend, cela économiserais du processeur ? non ?
    Certainement oui ! Je ne connaissait pas non plus, je vais m'y pencher !

    Citation Envoyé par ShaiLeTroll Voir le message
    Il me semble que ReceiveBuf renvoie le nombre d'octet reçu, il sera justifié de l'utiliser !
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ReadResponse := TCPClient.ReceiveBuf(Response^, TRAME_LENGTH) > 0;
    Très juste, une gestion d'erreur en plus ne fait pas de mal !

    Citation Envoyé par ShaiLeTroll Voir le message
    Ensuite, le i-D9_BYTE, m'étonne, tu inverse le contenu du tableau ? <...>
    Explication :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    D9_BYTE = 8;
    D0_BYTE = 17;
    TRAME_LENGTH = 18;
    DATA_LENGTH = 10;
    ReadBuffer = array [0..TRAME_LENGTH] of Byte;
    AResponse = array [0..DATA_LENGTH] array of Byte;
    En fait, mes trames sont constitué de 18 bytes, le début étant l'heure d'envoie de la commande, la commande puis 10 bytes de données. Dans mon thread je lis ces 18 bytes, mais ne retourne que les données (qui sont les 10 derniers byte de ma commande, d'où le 'i-D9_BYTE' qui effectue simplement la copie mais décalé ... !

  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
    En fait, mes tableaux sont de tailles fixes
    biensur, le passage de paramètre de tableau fixe, il suffit de réclarer un type
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    TArrayFixe = array[0..100] of machin
    et c'est ce type qui passe en paramètre, c'est simplicime !

    mais mieux encore, Pourquoi ne pas déclarer ceci alors, je l'ai fait comme cela pour toutes mes communications machines via TCP/IP
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    type
      TTrameRec = packed record  
        Hour: array[0..7] of char; // HH:NN:SS ?
        CommandNum: array[0..9] of char; // 0123456789
      end;
    tu passe en paramètre un TTrameRec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ReadResponse := TCPClient.ReceiveBuf(MyTrameRec, SizeOf(TTrameRec)) > 0;
    Voir si @MyTrameRec est nécessaire


    et hop, tu peux lire facilement ton MyTrameRec comme des chaines, c'est juste plus compliqué à remplir voir ce [url=http://www.developpez.net/forums/showthread.php?t=393186]SUJET[url], cela concerne le fichiers, mais la manipulation de pointeur de record que ce soit sur fichier ou socket cela revient à la main chose


    tu as raison, pour le BiduleArray.ArrayLength - 1, tu m'excusera de l'erreur, entre TBidule et Byte, je me suis mélangé
    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 éclairé Avatar de femtosa
    Inscrit en
    Juin 2002
    Messages
    253
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 253
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    biensur, le passage de paramètre de tableau fixe, il suffit de réclarer un type
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    TArrayFixe = array[0..100] of machin
    et c'est ce type qui passe en paramètre, c'est simplicime !
    Je dirais même plus, c'est super simple !

    J'ai résolu mon problème ! J'ai utilisé comme tu l'as dis des types définit, et je passe le pointeur au thread qui effectue la lecture !

    ce qui donne en résumé :
    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
    //Mes types
    TCommandArray = array[0..(TRAME_LENGTH-1)] of Byte;
    TDatasArray = array[0..(DATAS_LENGTH-1)] of Byte;
    PCommandArray = ^TCommandArray;
     
    //Le renseignement du champ dans le thread
    procedure TTCPThread.SetResponseTab(AResponse: PCommandArray);
    begin
      Response := AResponse;
    end;
     
    //Et le thread
    procedure TTCPThread.Execute;
    begin
      while(not Self.Terminated) do
      begin
          HasResponse := TCPClient.ReceiveBuf(Response^, TRAME_LENGTH) > 0;
          Suspend;
      end;
    end;
    J'ai modifié la méthode 'Execute' du thread pour économisé du temps processeur, comme tu l'as proposé !
    Avec mon 'DoRead' comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    procedure TTCPThread.DoRead;
    begin
      Resume;
    end;
    Citation Envoyé par ShaiLeTroll Voir le message
    mais mieux encore, Pourquoi ne pas déclarer ceci alors, je l'ai fait comme cela pour toutes mes communications machines via TCP/IP
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    type
      TTrameRec = packed record  
        Hour: array[0..7] of char; // HH:NN:SS ?
        CommandNum: array[0..9] of char; // 0123456789
      end;
    tu passe en paramètre un TTrameRec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ReadResponse := TCPClient.ReceiveBuf(MyTrameRec, SizeOf(TTrameRec)) > 0;
    Voir si @MyTrameRec est nécessaire


    et hop, tu peux lire facilement ton MyTrameRec comme des chaines, c'est juste plus compliqué à remplir voir ce SUJET, cela concerne le fichiers, mais la manipulation de pointeur de record que ce soit sur fichier ou socket cela revient à la main chose
    C'est effectivement une solution que je trouve très élégante ! Je n'ai malheureusement pas le temps de me pencher plus dessus vu les délais de livraison, mais c'est une des améliorations futures possible pour les mises à jour ! Et c'est un concept que je garde bien au chaud pour les futurs projets !

    Merci beaucoup pour ton aide et ton code ! Ca a été très constructif !

    Bon développement !

  6. #6
    Expert confirmé

    Avatar de sjrd
    Homme Profil pro
    Directeur de projet
    Inscrit en
    Juin 2004
    Messages
    4 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Suisse

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2004
    Messages : 4 517
    Par défaut
    ?
    sjrd, ancien rédacteur/modérateur Delphi.
    Auteur de Scala.js, le compilateur de Scala vers JavaScript, et directeur technique du Scala Center à l'EPFL.
    Découvrez Mes tutoriels.

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

Discussions similaires

  1. [WinForms]Comment partager des objets entre threads ?
    Par AiSpirit dans le forum Général Dotnet
    Réponses: 3
    Dernier message: 16/08/2006, 08h57
  2. [javaBean + JSP] comment partager un objet entre les page JSP
    Par subzero82 dans le forum Servlets/JSP
    Réponses: 4
    Dernier message: 31/05/2006, 18h48
  3. [JTabbedPane] partager des objet
    Par biozaxx dans le forum AWT/Swing
    Réponses: 5
    Dernier message: 21/03/2006, 14h10
  4. [debutant] run() dans l'objet thread ou l'objet cible ?
    Par slim dans le forum Concurrence et multi-thread
    Réponses: 2
    Dernier message: 18/01/2006, 23h06
  5. [Tomcat][Plusieurs applications WEB] Partage d'objet.
    Par ZeKiD dans le forum Tomcat et TomEE
    Réponses: 8
    Dernier message: 09/04/2005, 13h23

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