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 :

multi-threads et multi-core


Sujet :

Langage Delphi

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    72
    Détails du profil
    Informations personnelles :
    Âge : 57
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 72
    Par défaut multi-threads et multi-core
    Bonjour,

    Je fais quelques tests de charge sur les 4 core de mon PC (Windows Seven) et je suis un peu surpris... J'ai créé 4 threads qui effectuent des opérations en virgule flottante et ... je ne vois que le core 1 (le 2e) qui mouline à 75% tandis que les 3 autres sont au maximum à 5%.

    Cela semblerait indiquer que c'est à moi de répartir les threads sur chaque coeur, mais comment faire? Je pensais que Windows le ferait... Si ce n'est pas le cas, c'est plutôt génant pour les applis multi-threads, si tout part dans un coeur tandis que les 3 autres ne font pas grand chose.

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

    Informations forums :
    Inscription : Octobre 2002
    Messages : 707
    Par défaut
    Si tu y comprends quelque chose...

    http://synopse.info/forum/viewtopic.php?pid=205

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    72
    Détails du profil
    Informations personnelles :
    Âge : 57
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 72
    Par défaut
    Je survole... Ce que je comprend est que c'est bien au delà de la compétence et du temps que l'on peut mettre dans un projet, celui que je fais en tout cas.

    Une discussion intéressante sur les threads et les multi-coeurs en Delphi:

    https://forums.embarcadero.com/messa...ssageID=347942

    Pas trés positive non plus mais moins pessimiste quand même. Pour résumer, il y a bien un soucis d'utilisation des multi-coeurs, mais si l'on accepte un certain délai, ils seront quand même utilisés. Windows ne peut préempter le thread en exécution (celui qui en créer un second) pour activer le nouveau thread sur un nouveau coeur, mais le fera quand le premier donnera la main... C'est déjà cela. L'utilisation de Sleep(10) est donc fortement conseillée.

    Sachant que j'ai besoin de beaucoup de threads en parallèle, mais que ceux ci font des traitements longs, cela devrait m'aller. Encore que j'ai un doute... 4 threads tournant en continu pendant plusieurs minutes, et je ne vois pas ce transfert sur d'autres cœurs. J'avoue que j’atteins la limite de mes compétences limitées sur le sujet, ici.

  4. #4
    Membre émérite
    Avatar de ouiouioui
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Août 2006
    Messages
    993
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 993
    Par défaut
    J'ai fait un projet test, j'ai un double coeur, si je test avec un thread le processeur est utilisé à 50% mais la charge est répartie sur les 2 coeurs. avec 2 threads les 2 coeurs sont à 100%

    tes threads utilise des fichiers sur le disque ou s'échange des objects ?

    http://www.toutenvrac.org/Stress.zip

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

    Informations forums :
    Inscription : Octobre 2002
    Messages : 707
    Par défaut
    Compilé avec quelle version de Delphi ?

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    72
    Détails du profil
    Informations personnelles :
    Âge : 57
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 72
    Par défaut
    Avec une version plus récente que la mienne il semblerait... Je n'ai pu executer son code en le recompilant, mais je viens de comprendre mon soucis. Je faisais en systématique un Synchronize dans chaque thread, ce qui du coup invalidait tout intérêt à avoir threadé l'application il semblerait!

    En enlevant le Synchronize, j'obtiens bien le même résultat ... et même quelque chose d'intéressant, 2 threads se répartissent sur 4 core, pour un total CPU de 50%. 4 threads prennent 100% du CPU de 4 cores.

    J'aurais tendance à dire 'résolu'...

  7. #7
    Membre Expert

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 47
    Localisation : France, Rhône (Rhône Alpes)

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

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Par défaut
    Citation Envoyé par Pocus Voir le message
    mais je viens de comprendre mon soucis. Je faisais en systématique un Synchronize dans chaque thread, ce qui du coup invalidait tout intérêt à avoir threadé l'application il semblerait!
    Grosses erreur en effet.
    Synchronize consiste en fait à poster un message au thread principal depuis le thread secondaire, puis à suspendre le thread secondaire en attendant que le thread principale traite le message, exécute le traitement et relance le thread secondaire.
    Ca avait un intérêt du temps des machines monoprocesseur/monocoeur puisque de toute façon, il ne pouvait y avoir qu'un seul thread utilisant le CPU à un instant donné, mais avec un véritable multi-coeurs... et bien il n'y a qu'un seul thread qui utilise le CPU...

    Aujourd'hui, je dirais que Synchronize est à proscrire...

  8. #8
    Membre confirmé
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    72
    Détails du profil
    Informations personnelles :
    Âge : 57
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 72
    Par défaut
    En effet, j'en suis venu à cette conclusion également, Synchronize n'a que peu d'intérêt désormais.

    J'ai un peu avancé dans mon projet et je me pose des questions encore, si vous voulez bien m'éclairer.

    J'ai une TImage sur ma fiche. J'affiche dedans des images, via un thread, qui a un évènement 'OnTimer' déclaré dans la fiche. Je me pose un peu la question 'à qui appartient quoi'.

    Voici mon 'chrono'
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
      TChrono = class(TThread)
      protected
        FOnTimer: TNotifyEvent;
     
      public
        Interval : integer;
        property OnTimer: TNotifyEvent read FOnTimer write FOnTimer;
        constructor Create(CreateSuspended:boolean = True; aIntervalInMs : integer = 100);
        destructor Destroy; override;
     
      protected
        procedure Execute; override;
      end;
    Le OnTimer est donc branché sur une procédure déclarée dans la fiche. Donc quid, la procédure (executée dans le thread, mais déclarée dans la fiche) fait partie du thread principal, oui ou non (et si oui, c'est donc dangereux?) Là je m'y perd.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    procedure TMain.FormCreate(Sender: TObject);
    var i : integer;
    begin
      (..)
     
      Timer := TChrono.Create(True, 40);
      Timer.OnTimer := OnTimer;
     
    Lock := TMultiReadExclusiveWriteSynchronizer.Create;
    Par soucis d'exhaustivité voici le code du 'OnTimer'

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    procedure TMain.OnTimer(Sender: TObject);
    var i : integer;
    begin
      try
        Lock.BeginWrite;
        Image.Canvas.Lock;
        Image.Canvas.Draw(0, 0, MonImage);
        Image.Canvas.UnLock;
        Inc(Frame);
      finally
        Lock.EndWrite;
      end;
    end;
    Je ne suis pas sûr de comprendre correctement les choses, j'ai besoin de réponses éclairées.

    1. Le timer est une procédure de la fiche, mais elle sera exécutée dans le thread. J'ai donc mis un joli MREW (TMultiReadExclusiveWriteSynchronizer) en plus, est il utile?

    2. Ce MREW est une variable globale, fallait il le déclarer plutôt en variable de la fiche? Quelle portée est la meilleure? A déclarer dans le thread, dans la fiche, en global? Je dirais qu'il doit avoir la portée de ce que l'on manipule, donc si on manipule des variables de la fiche, il faut le déclarer dans le fiche?

    3. A noter que si je ne lock pas le canvas (via Canvas.Lock), rien n'apparait

    Bref, questionnements divers... Pour info l'appli tourne dans 95% des cas, et quand elle le fait, elle peut le faire pendant une heure et plus. Mais dans 5% des cas, elle freeze au démarrage... J'ai donc un doute... Toute aide sera appréciée!

  9. #9
    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
    Synchronize n'est PAS inutile, mais comme son nom l'indique il synchronise l'execution d'une méthode avec le Thread principal.


    si FOnTimer est appelé depuis TThread.Execute, le code s'execute dans le thread secondaire évidemment. Peu importe l'endroit où est déclaré le code.

    Le code de la fiche "n'appartient" à aucun Thread. Le thread principale boucle sur une fonction de traitement de messages qui va régulièrement solliciter les méthodes de TForm (depuis le thread principal donc)..c'est d'ailleurs la raison pour laquelle il ne faut pas interagir avec la VCL depuis un thread secondaire car il risque d'y avoir collision.

    C'est pour cela que TThread propose une fonction synchronize qui permet d'exécuter un code dans le thread principal et donc sans collision possible (à utiliser par exemple en fin d'execution pour exploiter les données traitées).

    Une autre solution est d'utiliser PostMessage() vers le Handle de la fiche, ça permet d'envoyer un signal à celle-ci sans interrompre l'exécution du thread secondaire. Très pratique pour mettre à jour une barre de progression PostMessage(Handle, WM_USER, Position, Total);
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    procedure TForm1.WMUser(var msg: TMessage); // message WM_USER
    begin
      ProgressBar1.Max := msg.lParam;
      ProgressBar1.Position := msg.wParam;
    end;
    SendMessage() aura par contre les mêmes conséquences que Synchronize puisque cette fonction attend la réponse du destinataire.

    Il existe aussi des objets de synchronisation spécifiques (CriticalSection, Mutex, Event, Semaphore...) qui ont chacun leur utilité.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  10. #10
    Membre confirmé
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    72
    Détails du profil
    Informations personnelles :
    Âge : 57
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 72
    Par défaut
    Le soucis est que la totalité du traitement appartient au thread secondaire, vu qu'il s'agit d'afficher des images = faire gérer l'affichage par un thread et non pas le thread principal.

    De ce que tu me dis, c'est totalement à proscrire? Pas d'interaction avec la VCL à partir d'un thread secondaire... Quid de l'intérêt de mettre une section critique (ou un MREW, la forme plus fine) alors? Ca ne peut pas permettre un bon fonctionnement?

    Et donc, qu'en est il de TCanvas.Lock ou Unlock? Il s'agit bien pourtant de permettre une interaction avec un object VCL...

    Appelez Lock dans une application multithread pour empêcher d'autres threads d'utiliser le canevas. Lock empêche l'exécution des autres threads coopératifs de l'application jusqu'à ce que la méthode Unlock soit appelée. Les appels imbriqués à Lock incrémentent la propriété LockCount afin que le canevas ne soit libéré que lorsque le dernier verrou est libéré.

    Dans des applications multithread utilisant Lock pour protéger un canevas, tous les appels utilisant le canevas doivent être protégés par un appel à Lock. Tout thread ne verrouillant pas le canevas avant de l'utiliser introduire des problèmes potentiels.


    Mutex, Semaphore, sections critiques, MREW... On parle bien quasiment de la même chose au final. Ca ne changera pas le problème, si problème il y a (de l'interaction d'un thread sur des composants VCL). Si je protège mon thread principal, ca sera par MREW (ou section critique), je n'ai pas besoin de mécanismes inter processus.

  11. #11
    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
    VCL pour sa partie Visuelle.

    un Canvas n'est pas forcément lié à un TWinControl

    d'autre part (sur D6) Lock ne fait qu'incrémenter un compteur protégé par un section critique...pas certain de l'intérêt.

    D'autre part l'affichage se fait dans le thread principal (qui traite WM_ERASEBKGND, WM_PAINT) si tu dessines dans un thead secondaire comment cela peut-il rester cohérent ?
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  12. #12
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 096
    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 096
    Par défaut
    Citation Envoyé par Pocus Voir le message
    De ce que tu me dis, c'est totalement à proscrire? Pas d'interaction avec la VCL à partir d'un thread secondaire...
    C'est surtout pour les TWinControl comme TForm, TMemo, TProgressBar ou TStatusBar, si tu la modifie dans un thread alors que Windows fait quelque chose avec, OS ERROR 1400 garanti !

    Pour les composants graphiques, comme le TLabel, l'utilisation en Thread de son Canvas doit être fait avec Lock, je ne l'ai jamais essayé, à tenter ...
    après tout, tu as raison, c'est dédié à ça !

    Perso, j'ai toujours utilisé un sytème de contexte (un record qui contient toutes les infos d'affichage) et un synchronize

    Maintenant, il est rare que mes threads affichent directement, en général, ils tourne, discute en TCP\IP, RS232 ou 422 avec d'autres périphériques, utilisent une DB embarquée pour stocker leur état et envoie des notifications aux abonnés, ces derniers pouvant être d'autres threads dont le thread principal qui va afficher les infos
    Dans l'application que je maitiens depuis peu, c'est en GDI+, et la gestion de thread, disons que c'est un peu à l'arrache et ça tourne semble-t-il très bien !

    Citation Envoyé par Pocus Voir le message
    Le soucis est que la totalité du traitement appartient au thread secondaire, vu qu'il s'agit d'afficher des images = faire gérer l'affichage par un thread et non pas le thread principal.
    Comme le dessin, c'est WM_PAINT, c'est le thread principal qui le reçoit et il appel les méthodes de dessin (invalidate, paint...) qui sont nécessaire, si dans un autre thread, tu modifie un objet genre Font, son Handle change en plein milieu d'une utilisation, la loi de murphy te garanti une explosion !
    En théorie, si tu as été rigoureux avec un Lock systèmatique sur chaque objet, cela doit fonctionner sans problème (si la VCL elle même est 100% thread-safe)

    Ensuite, quand tu débutes la manipulation Thread + VCL et que tu lis l'aide comme moi lorsque j'ai fait mon 1er Thread

    Si plusieurs threads actualisent les mêmes ressources, synchronisez les threads pour éviter les conflits.

    La plupart des méthodes accédant à un objet VCL ou actualisant une fiche doivent être appelées dans le thread principal VCL ou utilisez un objet de synchronisation tel que TMultiReadExclusiveWriteSynchronizer.
    Tu ne prends pas de risques et tu écoutes les conseils de ceux qui ont écrit un bouquin avant même que tu saches taper sur un clavier avec plus de deux doigts !

    Mais tu as une solution alternative !

    En fait, il te faut 2 images, une image utilisé par le thread principal et une autre utilisé par le thread secondaire, avec une section critique pour protéger la permutation et l'accès aux tampons

    Tient comme dans le sujet [Thread] et [TImage]
    Et surtout le sujet Problèmes étranges avec le dual core

    Pour info, c'est comme cela en DirectDraw, on travaille sur une image, une fois terminé, on permute son image avec un autre tampon libre, et cela en boucle, ainsi durant qu'un thread affiche sur l'écran (opération assez lente), un autre peu dessiner sur un tampon, faire tourner des calculs ...

    l'avantage c'est que le thread peut donc tourner à fond les ballons, pas de lock, pas de synchronize, cela débite de l'image tout le temps, ensuite, c'est juste le thread principal qui doit suivre la cadence (il reçoit une notification qui lui dit qu'il doit bosser, ou alors un Timer ou même OnIdle)
    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 Expert

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 47
    Localisation : France, Rhône (Rhône Alpes)

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

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Perso, j'ai toujours utilisé un sytème de contexte (un record qui contient toutes les infos d'affichage) et un synchronize
    Si tu fais ça, tu arrêtes le thread secondaire le temps de mettre à jour l'affichage dans le thread principal.
    Personnellement, cette approche me pose deux problèmes :
    - Arrêter le thread secondaire pour que le principal mette à jour l'affichage, je trouve que c'est un comble lorsqu'on fait du multi-threading. L'intérêt du multi-threading c'est que tout le monde bosse en même temps. Idéalement même, que l'affichage se fasse lorsque le thread secondaire est bloqué en attente d'E/S. Ici si le thread principale était occupé au moment du synchronize, le thread secondaire peut resté bloqué un moment...
    - Il y a un problème dans le sens des dépendances. Logiquement, le thread secondaire implémente le métier, le thread principale l'IHM. Or l'IHM doit être construite par dessus le métier. Le métier ne doit savoir que l'IHM existe. Ici, c'est le métier qui pilote la mise à jour de l'affichage en appelant le code de mise à jour de l'affichage dans le synchronize...

    Moi je fais plutôt l'inverse : Je laisse les informations d'états nécessaire à l'affichage dans le thread secondaire.
    Ce dernier se contente de poster une notification à celui qui la demande (avec un vulgaire PostMessage sur un Handle de fenêtre fournit à la création du thread secondaire).
    Lorsque le thread principale décide de mettre à jour l'affichage (en réponse à la notification, sur un timer...), il va lire les informations d'états nécessaires auprés du thread secondaire. Bien évidemment, ces dernières sont protégées par une section critique.
    De cette façon, le thread secondaire n'est pas rallenti le temps que le thread principale fasse sont boulo. Et lorsque le thread principal met à jour l'affichage, il peut disposer des informations les plus à jour...

    Pour info, c'est comme cela en DirectDraw, on travaille sur une image, une fois terminé, on permute son image avec un autre tampon libre, et cela en boucle, ainsi durant qu'un thread affiche sur l'écran (opération assez lente), un autre peu dessiner sur un tampon, faire tourner des calculs ...
    A la base, le double buffer n'est pas utilisé pour ça mais pour éviter les effets de clignotement et synchroniser l'affichage des buffers avec le ballayage de l'écran :
    La carte graphique (ce n'est pas un thread) affiche le premier buffer à la vitesse du ballayage.
    Pendant ce temps, tu dessines le nouvel écran dans un buffer arrière pour éviter par exemple que le ballayage ne passe et affiche le buffer entre le moment ou tu as dessiné le fond et le moment ou tu n'as pas encore dessiné tes sprites...
    Et si tu veux même que la bascule entre le buffer avant et le buffer arrière se fasse exactement au moment où le ballayage a terminé l'écran (la synchro Verticale), il te faut même un troisième buffer : Deux buffers prêt à être affichés au moment où la carte graphique le nécessitera, et un troisième sur lequel tu peux préparer tranquillement l'affichage.

  14. #14
    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
    Citation Envoyé par Franck SORIANO Voir le message
    Si tu fais ça, tu arrêtes le thread secondaire le temps de mettre à jour l'affichage dans le thread principal.
    non, dans le cas de traitement d'image, tu as
    1 - l'image dessinée par l'IHM
    2 - l'image en cours de traitement
    3 - l'image tampon entre les deux (ou une liste d'images tampon)

    quand le thread a terminé un traitement il copie son image dans le tampon et signale cette action à l'IHM avant de reprendre son boulot.

    quand l'IHM est notifié il copie l'image tampon dans son image active.

    il faut juste gérer un lock sur l'image tampon (ou la liste d'images tampon).

    Citation Envoyé par Franck SORIANO Voir le message
    Personnellement, cette approche me pose deux problèmes :
    - Arrêter le thread secondaire pour que le principal mette à jour l'affichage, je trouve que c'est un comble lorsqu'on fait du multi-threading. L'intérêt du multi-threading c'est que tout le monde bosse en même temps. Idéalement même, que l'affichage se fasse lorsque le thread secondaire est bloqué en attente d'E/S. Ici si le thread principale était occupé au moment du synchronize, le thread secondaire peut resté bloqué un moment...
    - Il y a un problème dans le sens des dépendances. Logiquement, le thread secondaire implémente le métier, le thread principale l'IHM. Or l'IHM doit être construite par dessus le métier. Le métier ne doit savoir que l'IHM existe. Ici, c'est le métier qui pilote la mise à jour de l'affichage en appelant le code de mise à jour de l'affichage dans le synchronize...

    Moi je fais plutôt l'inverse : Je laisse les informations d'états nécessaire à l'affichage dans le thread secondaire.
    Ce dernier se contente de poster une notification à celui qui la demande (avec un vulgaire PostMessage sur un Handle de fenêtre fournit à la création du thread secondaire).
    Lorsque le thread principale décide de mettre à jour l'affichage (en réponse à la notification, sur un timer...), il va lire les informations d'états nécessaires auprés du thread secondaire. Bien évidemment, ces dernières sont protégées par une section critique.
    De cette façon, le thread secondaire n'est pas rallenti le temps que le thread principale fasse sont boulo. Et lorsque le thread principal met à jour l'affichage, il peut disposer des informations les plus à jour...


    A la base, le double buffer n'est pas utilisé pour ça mais pour éviter les effets de clignotement et synchroniser l'affichage des buffers avec le ballayage de l'écran :
    La carte graphique (ce n'est pas un thread) affiche le premier buffer à la vitesse du ballayage.
    Pendant ce temps, tu dessines le nouvel écran dans un buffer arrière pour éviter par exemple que le ballayage ne passe et affiche le buffer entre le moment ou tu as dessiné le fond et le moment ou tu n'as pas encore dessiné tes sprites...
    Et si tu veux même que la bascule entre le buffer avant et le buffer arrière se fasse exactement au moment où le ballayage a terminé l'écran (la synchro Verticale), il te faut même un troisième buffer : Deux buffers prêt à être affichés au moment où la carte graphique le nécessitera, et un troisième sur lequel tu peux préparer tranquillement l'affichage.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  15. #15
    Membre confirmé
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    72
    Détails du profil
    Informations personnelles :
    Âge : 57
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 72
    Par défaut
    Ca me parait bien en effet... Ca laisse au thread principal l'affichage d'IHM (l'image en un bloc = écrit dans un buffer), et au thread secondaire le soin de composer ce buffer, ce qui est le plus onéreux (rappatriement des données, transcriptions en images à afficher, placées une par une dans un buffer).

    Merci à tous, vous m'avez bien éclairci la situation!

  16. #16
    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
    Citation Envoyé par Pocus Voir le message
    Ca me parait bien en effet... Ca laisse au thread principal l'affichage d'IHM (l'image en un bloc = écrit dans un buffer), et au thread secondaire le soin de composer ce buffer, ce qui est le plus onéreux (rappatriement des données, transcriptions en images à afficher, placées une par une dans un buffer).

    Merci à tous, vous m'avez bien éclairci la situation!
    selon le cas, la "copie" peut être aussi une copie de pointeur, pas forcément une copie de l'image

    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
     
    //Thread
      while not Terminated do
      begin  
         Image := TBitmap.Create;
         //< traitement de Image >
         EnterCriticalSection(lock);
         try
            Old := Tampon;
            Tampon := Image;
         finally
           LeaveCriticalSection(lock);
         end;
         PostMessage(Handle, WM_USER,0,0);
         Old.Free; // au cas ou il existe déjà (en dehors du lock tant qu'à faire)
      end;
     
    // IHM
    procedure TForm1.WMUser()
    begin
         Image.Free; // au cas ou elle existe déjà
         EnterCriticalSection(lock);
         try
            Image := Tampon;
            Tampon := nil;
         finally
           LeaveCriticalSection(lock);
         end;
    end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  17. #17
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 096
    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 096
    Par défaut
    @Pocus, l'idée c'est d'avoir 2 objets TBitmap qui circule, pendant que travaille sur l'un, l'autre est utilisé pour l'affichage, puis tu permutes juste les objets ... comme dans mes liens ou le code Paul Toth



    Citation Envoyé par Franck SORIANO Voir le message
    Si tu fais ça, tu arrêtes le thread secondaire le temps de mettre à jour l'affichage dans le thread principal.
    Personnellement, cette approche me pose deux problèmes :
    - Arrêter le thread secondaire pour que le principal mette à jour l'affichage, je trouve que c'est un comble lorsqu'on fait du multi-threading. L'intérêt du multi-threading c'est que tout le monde bosse en même temps. Idéalement même, que l'affichage se fasse lorsque le thread secondaire est bloqué en attente d'E/S. Ici si le thread principale était occupé au moment du synchronize, le thread secondaire peut resté bloqué un moment...
    Je suis tout a fait d'accord avec toi, comme le disait paul toth, il faut choisir le bon objet de synchro !
    Mais tous les exemples utilisent du synchronize, tous les débutant utilisent synchronize et tant que personnes ne te disent que c'était peu performant, tu peux l'utiliser longtemps !

    Par flemme, synchronize c'est pratique, si ton thread secondaire peu attendre, cela évite de se prendre la tête ...
    si tu fais du pseudo temps réel, là évidemment, faut réfléchir à un système de notification !


    [HS !!!]
    Citation Envoyé par Franck SORIANO Voir le message
    - Il y a un problème dans le sens des dépendances. Logiquement, le thread secondaire implémente le métier, le thread principale l'IHM. Or l'IHM doit être construite par dessus le métier. Le métier ne doit savoir que l'IHM existe. Ici, c'est le métier qui pilote la mise à jour de l'affichage en appelant le code de mise à jour de l'affichage dans le synchronize...

    Dans certains cas le monde paradisiaque du MVC, que je trouve difficile à mettre en place en Delphi, c'est tout simplement une utopie !


    Tu as donc la chance de travail sur un projet où il y a une forte séparation IHM et métier !
    Je ne l'ai vu qu'une seule fois en 11 ans de carrière et encore, il y avait des disgressions fréquentes !
    Sinon, j'ai souvent vu tout le code métier directement dans l'IHM, la plaie quand tu dois reproduire des manip SANS les écrans, parfois c'est tellement pénible de fouiller dans les 5000 lignes de code de l'écran, que tu utilise une TForm sans l'afficher (oui un comble), que tu appel un bouton (encore un comble) ...
    Parfois, à mon grand désespoir, il faut s'adapter à un code non architecturé mais qui fonctionne, tu fais avec, tu essayes de faire plus POO et Patterns dans les nouveaux Dev tout en essayant de ne pas trop choquer le reste de l'équipe par un code inhabituel par rapport à tout ce qui existe déjà depuis 15ans !

    Sinon tu as lu une phrase de mon sujet et tu ne lit pas la suite

    Citation Envoyé par ShaiLeTroll Voir le message
    Maintenant, il est rare que mes threads affichent directement, en général, ils tourne, discute en TCP\IP, RS232 ou 422 avec d'autres périphériques, utilisent une DB embarquée pour stocker leur état et envoie des notifications aux abonnés, ces derniers pouvant être d'autres threads dont le thread principal qui va afficher les infos
    Dans l'application que je maitiens depuis peu, c'est en GDI+, et la gestion de thread, disons que c'est un peu à l'arrache et ça tourne semble-t-il très bien !
    Parce que là on parle de thread, il m'arrive de le faire entre processus, l'IHM dans un exe ou plusieurs exe (un notify icon, en général, je l'isole), utilisant du COM, DCOM, Service NT ou Web, ... le métier totalement déporté !

    Il ne faut généraliser tous problèmes de la même façon !
    Par exemple, j'ai un objet TNotifyXChatLogFileChangeThread qui encaspule un FindFirstChangeNotification, j'ai un event OnChange et OnIdle, l'appel est fait via Synchronize, car le thread n'a pas besoin de continuer son scan tant que l'on a pas déjà pris en compte celui en cours !
    Le Synchronize est très utile dans ce cas !
    Le but étant d'afficher en temps réel le delta de données entre deux mis à jour des fichiers log de conversation de XChat, c'était en 2006, lorsque XChat était encore gratuit et n'avait pas de fonctionnalité de notification par infobulle
    Je pouvais suivre dans la bulle une conversation sans même avoir besoin de lire XChat ...

    En plus dans ce projet, l'exe n'est qu'une simple NotifyIcon et un menu, le programme ne fait absolument rien qu'afficher la Bulle
    Ce qui est encore plus interessant, c'est que lors que l'utilisateur utilise le Menu, cela bloque le Idle et donc synchronize, ainsi l'utilisateur n'est pas géné par une bulle qui cacherait le menu !

    Chaque problème à une solution, la généralisation par l'absolu vérité n'existe pas !

    J'utilise rarement Synchronize, c'est vrai, mais c'est déjà un premier pas dans la gestion inter-thread, une fois la notion comprise on peut passer à des mécanismes plus performant si il sont adaptés !
    personnellement j'utilise parfois TMultiReadExclusiveWriteSynchronizer ou TCriticalSection mais beaucoup de TThreadList

    Autre cas où la séparation IHM\Métier est difficile !
    Je devais pour un projet afficher des Infobulles HTML sur des controls Disabled alors qu'il ne provoque jamais de OnHint !
    Au départ, je me suis dit TTimer ou TThread ?
    Au final, j'ai tellement de synchronize pour les show\Close sur une TForm ou encore FindDragTarget\ControlAtPos que le TThread n'était pas utile, en fait j'en sais rien et je ne le saurais jamais, le code a été pondu, installé, satisfait le client et j'ai changé de boulot depuis ...

    Je pense que l'on est nombreux à coder selon l'humeur car difficile de suivre une spec inexistante, de respecter un modèle oublié ... UML euh c'est quoi ?

    Par exemple, dans un projet en 2002 de pilotage de robot, j'ai eu des problèmes pour lancer l'impression de code-barre sur une imprimante d'étiquette depuis un thread, QuickReport bloquait sur le Print, je n'avais pas envie de me battre avec !
    J'ai donc utilisé un record de contexte, une threadlist utilisé comme une FIFO, les threads ajoutent une demande d'étiquette, un timer se charge de prendre les demandes une par une et des les imprimer
    Est-ce que QuickReport c'est de l'IHM ? mais l'impression de l'étiquette c'est du métier, c'est dans le process de l'automate !

    [HS END]



    Citation Envoyé par Franck SORIANO Voir le message
    Moi je fais plutôt l'inverse : Je laisse les informations d'états nécessaire à l'affichage dans le thread secondaire.
    Ce dernier se contente de poster une notification à celui qui la demande (avec un vulgaire PostMessage sur un Handle de fenêtre fournit à la création du thread secondaire).
    Lorsque le thread principale décide de mettre à jour l'affichage (en réponse à la notification, sur un timer...), il va lire les informations d'états nécessaires auprés du thread secondaire. Bien évidemment, ces dernières sont protégées par une section critique.
    De cette façon, le thread secondaire n'est pas rallenti le temps que le thread principale fasse sont boulo. Et lorsque le thread principal met à jour l'affichage, il peut disposer des informations les plus à jour...
    On est tout à fait d'accord !
    Désolé Pocus, d'envahir ton sujet !
    Sinon quel est le but réel ?
    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
    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
    ceci dit la séparation métier/IHM n'est pas liée au threading...tu peux (et doit) séparer les deux même si ton application est monothread.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  19. #19
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 096
    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 096
    Par défaut
    [HS]
    Oui, la séparation IHM\Métier n'a rien à avoir avec le thread ou non d'où le [HS !!!] !
    Mais comme on parle de VCL dans un thread et "calcul" dans un autre thread, cela vient naturellement !

    Paul Toth, je ne sais pas combien de société tu as traversé dans ta longue carrière, surement plus que moi !
    Combien de développeur appliquait ce dogme ?
    Combien de développeur avait le niveau pour l'appliquer ?
    Combien de développeur savait ce qu'était le MVC ?
    Combien de développeur créait des objets hérités d'autre chose que TForm ou TDataModule ?
    Je les comptes sur les doigts d'une main !

    Mais bon, 2 PDG de 2 sociétés différentes à 5 ans d'intervalle m'ont dit que j'étais :
    - trop perfectionniste
    - trop intransigeant
    - manquant d'humilité !
    c'est que ça doit être vrai
    c'est mon côté TROLL !

    il me manque plus que la canne !

    [HS END]


    Désolé encore Pocus !
    N'hésite pas à revenir pour nous expliquer plus précisément ton fonctionnel !
    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

  20. #20
    Membre émérite
    Avatar de ouiouioui
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Août 2006
    Messages
    993
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 993
    Par défaut
    Sinon depuis Delphi XE il y a la méthode Queue c'est un Synchronise non bloquant.
    Pour l'envoie de petite donnée a des durée pas trop rapproché sa va pas mal.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Queue(procedure begin fmMain.ProgressBar.Position := I; end);
    C'est pratique on peut envoyer une methode anonyme.

    Dans le cas de Pocus je ne pense pas que sa irait vu qu'il n'a apparemment pas XE

Discussions similaires

  1. Multi-threading, programmation multi-coeur
    Par Napech dans le forum C
    Réponses: 0
    Dernier message: 17/06/2010, 21h17
  2. multi threading sur multi coeurs
    Par Pocus dans le forum Langage
    Réponses: 8
    Dernier message: 26/03/2010, 11h43
  3. Réponses: 15
    Dernier message: 22/12/2008, 22h01
  4. [BP7] Multi-cpu, multi-core, multi-thread et programme Pascal
    Par Transgarp dans le forum Turbo Pascal
    Réponses: 6
    Dernier message: 07/04/2008, 18h43
  5. Réponses: 1
    Dernier message: 23/05/2005, 15h52

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