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

MFC Discussion :

Communication entre des threads


Sujet :

MFC

  1. #1
    Membre averti
    Inscrit en
    Juillet 2007
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Juillet 2007
    Messages : 20
    Par défaut Communication entre des threads
    Bonjour à tous,
    Je suis entrain de développer un logiciel sous Visual Studio en C++ à base de fenêtres Dialog.
    Je souhaite communiquer entre une fenêtre et un processus lancer à partir de celle-ci.
    Je lance le processus de la façon suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    CRuntimeClass* m_pThread = RUNTIME_CLASS(TagDetectThread);
    AfxBeginThread(m_pThread,THREAD_PRIORITY_NORMAL,0,0,NULL);
    TagDetectThread étant la classe qui contient le processus.
    Je comptais utiliser les fonctions suivantes mais je n'arrive pas a les faire fonctionner :
    PostThreadMessage
    RegisterWindowMessage
    ON_REGISTERED_THREAD_MESSAGE


    A la suite d'un "click" sur un bouton, je veux déclencher la fin du thread.
    J'utilise donc la fonction suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    DWORD ID;
    ID = (ULONG)GetCurrentThreadId;
    PostThreadMessage(ID,WM_QUIT,0,0);
    Dans la classe comportant mon thread, j'ajoute juste après la boite de message :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    [i]ON_THREAD_MESSAGE(WM_QUIT,Run)       // Run étant la fonction du thread
    Dans cette fonction Run, je met une fonction de détection de cette forme :
    MSG msg;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    if(GetMessage(&msg,NULL,0,0)<0) AfxMessageBox("Error");
    Malheureusement elle ne fonctionne pas. Etant donné que je débute en MFC - C++, je ne comprends pas pourquoi celà ne marche pas ?
    Ais-je oublié quelque chose ? Y a-t-il quelque chose de mal défini ?
    Merci beaucoup pour votre aide.

    utilise la balise code, Merci Farscape

  2. #2
    Membre éclairé
    Inscrit en
    Février 2006
    Messages
    256
    Détails du profil
    Informations forums :
    Inscription : Février 2006
    Messages : 256
    Par défaut
    Salut,

    supposition (1):
    Tu dis que tu "lance le processus", donc ce doit être un exécutable.
    Ce que tu peux faire:
    Tu déclare une variable membre qui sera le handle de ton processus lancé avec ( supposition tjrs ) CreateProcessus( ) lors d'un clic sur un bouton de commande par exemple.
    Lorsque tu voudras le manipuler avec les autres fonction de l'API, tu pourras tjrs te servir du handle du processus.

    supposition (2):
    Tu parles aussi de Thread, donc là on est "plus" ds une fonction.
    Pareil que ci-dessus, mais avec CreateThread( ).

    Si tu as la MSDN, faire un F1 sur CreateProcessus et CreateThread qui t'expliquera les autres fonctions de manipulation des processus/thread.

    C'est pas du pur MFC ( plutôt de l'API Win32 ) mais ça peut te sortir d'affaire.


  3. #3
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 391
    Par défaut
    Déjà, ton thread est-il un vrai thread UI, avec boucle de messages et tout, ou bien un thread qui fait un traitement et qui n'est pas supposé avoir de boucle de messages ?
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  4. #4
    Membre averti
    Inscrit en
    Juillet 2007
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Juillet 2007
    Messages : 20
    Par défaut
    Je ne vois pas trop ce que tu entends par Thread UI, mais en gros la classe TagDetectThread est dérivée d'une classe CWinThread, et elle comporte une partie réception des messages (avec AFX) et une instance RUN dans laquelle j'ai mis le code a executer par le thread.
    Concernant la fonction de ce thread, il doit juste se terminer quand on lui demande et s'intérompre sur demande. Après je en sais pas si celà nécéssite forcément une boucle de message.


    Pour Denn's, je suis dans la supposition (2) et je n'ai pas de problème pour lancer le thread. Je cherche a savoir comment lui faire prendre en compte un message qui vient d'une classe CDialog.
    Le problème n'est pas de savoir quelles sont les fonctions dont j'ai besoin, mais plutôt comment les faires fonctionnées ?

  5. #5
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 391
    Par défaut
    Ce que tu me décris, c'est un thread UI.

    C'est donc le genre de thread que tu peux terminer avec un PostThreadMessage(WM_QUIT, etc.) ou avec un message "registered" dans le traitement duquel tu appellerais PostQuitMessage().

    Généralement, je laisse l'EDI généré automatiquement les handlers pour les messages enregistrés pour les fenêtres, mais j'ignore si ça marche pour les CWinThread aussi (et j'en doute...)
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  6. #6
    Membre averti
    Inscrit en
    Juillet 2007
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Juillet 2007
    Messages : 20
    Par défaut
    Effectivement ca marche bien pour les fenêtres mais pour les threads je ne crois pas que ça soit possible. Ca va être dur d'avancer dans ces conditions ...

  7. #7
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 391
    Par défaut
    Au pire, tu utilises l'assistant pour un message Registered à une fenêtre, puis tu adaptes au thread (usage de la macro, fonction avec retour void, etc.)
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  8. #8
    Membre émérite Avatar de homeostasie
    Homme Profil pro
    Inscrit en
    Mai 2005
    Messages
    939
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 939
    Par défaut
    Citation Envoyé par Analog13
    Je ne vois pas trop ce que tu entends par Thread UI
    UI pour User Interface -> un thread d'interface utilisateur. A différencier avec le "worker thread" pour thread de travail.

    En gros, le premier traitre tout ce qui est interaction avec l'utilisateur (événement souris, raccourcis claviers, menu etc...) et le second est utilisé pour effectuer une tache/opération particulière (E/R de données, calcul etc...).


  9. #9
    Membre averti
    Inscrit en
    Juillet 2007
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Juillet 2007
    Messages : 20
    Par défaut
    C'est plutot un Worker Thread qui sert à faire un scan en continu.
    Je n'ai toujours pas trouvé comment l'arrêter.
    Le problème est que je n'arrive pas à faire le lien entre la classe qui le créé, et le thread proprement dit.
    Je pense que celà vient du fait que ce sont deux fichiers distincts et que le message privé ne se transmet pas de l'un vers l'autre.
    Depuis que je fais des recherches, j'ai trouvé beaucoup d'exemples, mais aucun n'est vraiment clair pour un débutant.
    Est ce que vous pouvez m'indiquer comment transmettre un message de ma CDialog vers mon thread ?

  10. #10
    Membre chevronné Avatar de stephdim
    Profil pro
    Inscrit en
    Août 2007
    Messages
    462
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 462
    Par défaut
    Bonjour,

    Quand tu ecrits ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    DWORD ID;
    ID = (ULONG)GetCurrentThreadId();
    PostThreadMessage(ID,WM_QUIT,0,0);
    C'est pas l'ID du thread appelant qu'il faut, mais l'ID du thread appelé.
    Et l'ID tu le trouves ici:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    CWinThread *pThread=AfxBeginThread(....);   // création du thread
     
    // Faut conserver le pointeur pThread pour l'utiliser dans CDialog
     
    // Dans CDialog:
     
    DWORD ID=pThread->m_nThreadID;
    PostThreadMessage(ID,WM_QUIT,0,0);
    Mais comme dit plus haut c'est lourd d'utiliser la pompe à message pour faire ce genre de synchronisation surtout s'il n'y a pas d'interface utilisateur (UI = fenetres ...)

    Regarde plutot ici:
    http://developpez.net/forums/showthread.php?t=390750

    @+

  11. #11
    Membre averti
    Inscrit en
    Juillet 2007
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Juillet 2007
    Messages : 20
    Par défaut
    Il y a une interface utilisateur mais pour l'instant elle n'agit pas sur le thread mis a part déclencher son arrêt.
    Une fois que j'aurai réussi à faire fonctionner l'arrêt du thread, il faudra que je trouve un moyen d'échanger des données entre ma classe "de base" et mon thread. Mais chaque chose en son temps.

    En ce qui concerne la boucle de message, il vaut mieux utiliser un Get ou un PeekMessage ?

  12. #12
    Membre chevronné Avatar de stephdim
    Profil pro
    Inscrit en
    Août 2007
    Messages
    462
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 462
    Par défaut
    Quand je parlais d'interface utilisateur, c'est une interface propre au nouveau thread, c'est a dire : une interface dans le thread d'origine et une autre interface dans le nouveau thread.

    Là je crois que tu melanges tout

    Tu as deux types de thread:

    * les worker threads --> c'est simplement une fonction qui est appelé, quand la fonction fait un 'return' le thread est fini. Apres dans cette fonction tu fais ce que tu veux, mais tu ne touches surtout pas aux fenetres windows. (car il n'y a pas de file à message de créé)

    * les UI threads --> c'est un thread avec une file à message. De là tu peux créer et gérer des fenetres comme dans une appli classique. Mais attention, une fenetre créée dans un thread ne peut pas etre manipulée par un autre thread (si, mais c'est limitée) --> confinement de la fenetre dans le thread qui l'a créée.

    Si ton thread n'a pas l'intention de créer des fenetres, utilises un worker thread.

    Si tu utilises un UI Thread, il faut surcharger les fonctions InitInstance() et ExitInstance() de CWinThread et y mettre ton code d'initialisation / nettoyage. --> dans InitInstance(), tu créées ta fenetre principale.

    La pompe a message est gérée par les MFC.

    Si ton thread n'a pas de fenetre, tu peux qd meme utiliser la file de message par l'intermediaire de la fonction PostThreadMessage(), et y mettre un gestionnaire coté thread (ON_THREAD_MESSAGE). Mais c'est lourd si c'est utilisé que pour des messages privés.

    Pour les échanges de données, tu peux utiliser une simple zone mémoire, mais il faut que tu 'serialises' les accès --> un seul thread ne doit pouvoir accèder aux données a la fois. --> mettre en place un mutex, ou une critical section.


    PS: le message WM_QUIT n'est jamais envoyé a son gestionnaire puisqu'il sert a sortir de la boucle à message. Met plutot ton code de fin de thread dans ExitInstance() ou créé un message privé (WM_USER+xxx)

    @+

  13. #13
    Membre averti
    Inscrit en
    Juillet 2007
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Juillet 2007
    Messages : 20
    Par défaut
    Merci pour cet éclaircissement limpide.
    Il s'agit donc bien d'un Worker Thread.
    Quand tu dis que c'est lourd d'utiliser la file de message, tu utilises quoi à la place ?
    Une fois que le lien est fonctionnel (enfin il semble l'être) comment fait on pour récupérer le message ? J'ai essayer d'utiliser GetMessage ou PeekMessage sans résultat ?? Peut être que celà fonctionne en se servant d'un WaitForSingleObject ?

    Est ce que l'utilisation d' AfxMessageBox dans le thread perturbe la distribution des messages ?

    Il existe de tutoriaux pour les Mutex ou Critical section ?

  14. #14
    Membre averti
    Inscrit en
    Juillet 2007
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Juillet 2007
    Messages : 20
    Par défaut
    Pour la boucle de message, comment faire pour que l'execution ne soit pas bloqué en attendant un éventuel message ?

    Voilà ce que j'ai utilisé :
    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
     
    #define WM_STOP 100
    ...
    ON_THREAD_MESSAGE(WM_STOP,Run)
    ...
     
    int TagDetectThread::Run()
    {
    MSG msg;
     
       while(GetMessage(&msg,NULL,0,0)
       {
             if(msn.message==WM_STOP)
             {
                  AfxMessageBox("Stop The Thread");
                  break;
             }
       }
     
    ...
    }
    Le problème c'est qu'il ne se passe rien tant que je n'envoi pas le message.
    Je souhaiterai rentrer dans la boiucle que lorsque l'on a envoyé un message.
    Comment procède-t-on ?

  15. #15
    Membre chevronné Avatar de stephdim
    Profil pro
    Inscrit en
    Août 2007
    Messages
    462
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 462
    Par défaut
    Bonsoir,

    Tu n'as pas a reecrire la boucle a message, elle est deja implementée par les MFC.

    En clair, tu mets ton code d'initialisation dans InitInstance() que tu as surchargé (fonction virtuelle de CWinThread). Et ton code de nettoyage dans ExitInstance(). (idem)

    C'est tout.

    Et entre le BEGIN_MESSAGE_MAP() et le END_MESSAGE_MAP(), tu déclares tes gestionnaires avec ON_THREAD_MESSAGE(...)

    Inutile de mettre un gestionnaire pour WM_QUIT, ce message n'est jamais dispatché (par DispatchMessage()), il sert de sentinelle pour sortir de la boucle à message.

    @+

  16. #16
    Membre averti
    Inscrit en
    Juillet 2007
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Juillet 2007
    Messages : 20
    Par défaut
    C'est bon j'ai trouvé une structure sembalble à celle que j'ai décrite hier, qui me permet d'arrêter le thread ou plutôt la boucle qu'il comporte.
    Si c'est possible, je souhaiterai avoir des infos sur ce dont parlait stephdim :

    "Pour les échanges de données, tu peux utiliser une simple zone mémoire, mais il faut que tu 'serialises' les accès --> un seul thread ne doit pouvoir accèder aux données a la fois. --> mettre en place un mutex, ou une critical section."

    Comment est ce qu'on met en place ce genre de zone mémoire ?
    Quel est le moyen le plus simple de transférer des données entre le hread et la fenêtre qui le commande ?

  17. #17
    Rédacteur
    Avatar de farscape
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    9 055
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2003
    Messages : 9 055
    Par défaut
    salut,
    dans le cas d'un thread de travail ,le sens de communication naturel est le thread vers l'interface.
    un message privé vers l'interface peut faire l'affaire.
    il faut aussi savoir que normalement on ne peut pas accéder aux objets MFC graphiques a partir d'un thread de travail, d'ou l'utilisation d'un message privé à l'interface.
    en fait ceci est vrai en debug, en release l'accès direct fonctionne même si ce n'est pas la méthode recommandée par MS.
    dans le cas de plusieurs thread tu peux utiliser un mutex pour acceder aux données:
    http://www.developpez.net/forums/sho...ighlight=mutex

  18. #18
    Membre averti
    Inscrit en
    Juillet 2007
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Juillet 2007
    Messages : 20
    Par défaut
    OK merci pour cet éclaircissement.
    Mais est ce que l'utilisation d'un message privé peut être valable pour transférer (ou accèder à) un tableau de structures ?
    Dois-je utiliser un mutex ? Faire un passage par pointeur ??

  19. #19
    Rédacteur
    Avatar de farscape
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    9 055
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2003
    Messages : 9 055
    Par défaut
    l'accès au tableau doit être protégé par le mutex.
    le tableau peut être une variable globale ou contenu dans un singleton.
    http://c.developpez.com/faq/vc/?page...GlobalVarInMFC
    dans ce contexte pas besoin de message, c'est juste un partage de données sécurisées.

  20. #20
    Membre averti
    Inscrit en
    Juillet 2007
    Messages
    20
    Détails du profil
    Informations forums :
    Inscription : Juillet 2007
    Messages : 20
    Par défaut
    Finalement, j'ai utilisé un buffer commun pour que la fenêtre puisse accèder aux données.
    Maintenant, j'ai un problème de communication entre mon thread et ma fenêtre.
    Je m'explique. Mon thread envoi un message à ma fenêtre pour faire une sorte de rafraichissement de l'affichage. Le problème est que je n'arrive pas a récuperer le message.

    Pour le récupérer, j'utilise une fonction timer pour venir analyser la boucle de message régulièrement, afin de voir si l'on a reçu le message d'affichage.
    Mais celui-ci n'arrive jamais.

    Voilà mon code :
    Pour l'envoi du message depuis le thread :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    #define WM_SHOW 103
     
    PostMessage(HWND_BROADCAST,WM_SHOW,0,0);
    Pour la réception depuis la fenêtre :
    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
     
    void OnTimer(UINT nIDEvent)
    {
         Display();
         CDialog::OnTimer(nIDEvent);
    }
     
    void Display()
    {
          MSG msg;
          PeekMessage(&msg,NULL,0,0,PM_REMOVE);
          if(msg.message==WM_SHOW)
          {
                //Affichage
          }
    }
    Si je mets une boucle d'attente comme celle là à la place de PeekMessage, le message est bien détecté mais la boucle est quasi-infini et l'application utilise toute la resource cpu.

    Boucle d'attente :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    while(msg.message!=WM_SHOW)
    {
           PeekMessage(&msg,NULL,0,0,PM_REMOVE);
    }
    Est ce que j'utilise convenablement PeekMessage ?
    Je suppose qu'il faut utiliser une autre méthode de synchronisation, mais comment procèder ? Sémaphore ?

Discussions similaires

  1. Réponses: 1
    Dernier message: 18/01/2008, 13h17
  2. variables partagées entre des threads
    Par Aragorc dans le forum Delphi
    Réponses: 27
    Dernier message: 06/10/2006, 15h12
  3. Communication entre 2 thread
    Par poxigua dans le forum GTK+
    Réponses: 3
    Dernier message: 08/08/2006, 16h59
  4. Communication entre 2 threads
    Par marsupilami34 dans le forum Langage
    Réponses: 4
    Dernier message: 26/08/2005, 15h08
  5. Communication entre deux Threads
    Par rvzip64 dans le forum Langage
    Réponses: 13
    Dernier message: 28/01/2005, 09h14

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