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 :

Bloquage entre CEvent et PostMessage


Sujet :

MFC

  1. #1
    Membre confirmé
    Inscrit en
    Janvier 2005
    Messages
    76
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Janvier 2005
    Messages : 76
    Par défaut Bloquage entre CEvent et PostMessage
    Bonjour,

    J'ai un problème dans une application multi-thread, je vais tenté de simplifier au maximum car j'ai beaucoup de code.

    Donc dans une CDialog, j'exécute 3 threads, qui ont chacun un lien avec la CDialog.

    A la fin de ces Threads j'envoie des messages privés pour exécuter des fonctions d'affichage et de traitements divers (calculs).

    Ces messages privés doivent être exécutés dans l'ordre c'est pourquoi je les ai synchronisé avec des évènements (CEvent)

    Mais lorsque que j'exécute mon appli, tout semble figé et ça dure indéfiniment.

    Mieux que des explications : du code -->
    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
    CDlg::CDlg()
    {
    	m_hAffichageEvent = CreateEvent(0, FALSE, FALSE, 0);
    	m_hCalculEvent = CreateEvent(0, FALSE, FALSE, 0);
    	m_hEnregistrementEvent = CreateEvent(0, FALSE, FALSE, 0);
    }
     
    BEGIN_MESSAGE_MAP(CDlg, CDialog)
    	//{{AFX_MSG_MAP(CDlg)
    	ON_WM_CTLCOLOR()
    	ON_WM_PAINT()
    	ON_BN_CLICKED(IDC_BTN_LANCER_ACQ, OnBtnLancerAcq)
    	//}}AFX_MSG_MAP
    	ON_MESSAGE( WM_TRAITEMENT,		OnTraitement) // Message privé
    	ON_MESSAGE( WM_CALCUL,			OnCalculer) // Message privé
    	ON_MESSAGE( WM_AFFICHAGE,		OnAfficher) // Message privé
    	ON_MESSAGE( WM_ENREGISTREMENT,	OnEnregistrer) // Message privé
    END_MESSAGE_MAP()
     
    void CDlg::OnBtnLancerAcq() 
    {
    	AfxBeginThread(ThreadAcquisition, this);
    	AfxBeginThread(ThreadArret, this);
    }
     
    void CDlg::OnEnregistrer(WPARAM wparam,LPARAM lparam)
    {
    	::SetEvent(m_hEnregistrementEvent);
    }
     
    void CDlg::OnAfficher(WPARAM wparam,LPARAM lparam)
    {
    	::SetEvent(m_hAffichageEvent);
    }
     
    void CDlg::OnCalculer(WPARAM wparam,LPARAM lparam)
    {
    	::SetEvent(m_hCalculEvent);
    }	
     
    void CDlg::OnTraitement(WPARAM wparam, LPARAM lparam)
    {
    	::PostMessage(this->m_hWnd, WM_CALCUL, 0, 0);
     
    	::WaitForSingleObject(m_hCalculEvent, INFINITE);
     
    	::PostMessage(this->m_hWnd, WM_AFFICHAGE, 0, 0);
     
    	::WaitForSingleObject(m_hAfficherEvent, INFINITE);
     
    	::PostMessage(this->m_hWnd, WM_ENREGISTREMENT, 0, 0);
     
    	::WaitForSingleObject(m_hEnregistrementEvent, INFINITE);
     
    	::CloseHandle(m_hCalculEvent);
    	::CloseHandle(m_hAffichageEvent);
    	::CloseHandle(m_hEnregistrementEvent);
    }
     
    UINT CDlg::ThreadAcquisition(LPVOID pvParam)
    {
    	CDlg  *pThis=reinterpret_cast< CDlg *>( pvParam) ;
    	srand(GetTickCount());
     
    	/* Du code*/
     
    	::PostMessage(pThis->m_hWnd, WM_TRAITEMENT, 0, 0);
     
    	/* Du code*/
     
    	return TRUE;
    }
    En mode Debug, ça bloque dès le premier WaitForSingleObject et ça n'en bouge plus, les messages privés ne sont même pas exécutés ... Je ne vois pas pourquoi, d'où ma requête.

    Merci pour votre aide, précieuse

  2. #2
    Membre confirmé
    Inscrit en
    Janvier 2005
    Messages
    76
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Janvier 2005
    Messages : 76
    Par défaut
    J'ai remplacé PostMessage par SendMessage et ça fonctionne ... Mais je me rappelle avoir lu qu'il fallait préférer poster un message (le placer dans la file d'attente) plutot que de l'envoyer (l'executer tout de suite).

    Notes: La fonction thread peut être définie dans une classe si elle est déclarée statique.
    Dans le cas d'utilisation conjointe avec des boîtes de dialogue modales, il est préférable de poster (PostMessage) le message plutôt que de l'envoyer (SendMessage), afin d'éviter un problème de réentrance avec la pompe à messages du thread principal.
    Ma boite de dialogue n'est pas modale dans mon cas.
    Pourtant avec PostMessage ça ne fonctionne pas ... Je ne comprend pas vraiment pourquoi ???

  3. #3
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    J'imagine que ton thread de calcul fait un postmessage vers le thread de la boîte de dialogue. Et à la réception du message, tu fais un SetEvent pour déclencher ton évènement. Malheureusement, ça ne marche pas tout à fait comme ça.
    Quand tu fais WaitForSingleObject, tu arrêtes littéralement l'exécution du thread jusqu'à ce que l'évènement soit déclenché. Donc, ce thread n'exécute plus sa boucle de message. D'où ton blocage.
    Question: mais pourquoi ça marche avec SendMessage et pas avec PostMessage (et pourquoi ces mises en gardes)? En fait, PostMessage met le message dans la file et à la boucle de message, celui-ci est extrait et traité. SendMessage ne met pas le message dans la file des messages mais déclenche le traitement dès son appel.Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message. et The PostMessage function places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.
    Normalement, la solution est plutôt quelque chose comme ça: ton thread de calcul fait le SetEvent plutôt que d'envoyer un message. En déclenchant ton évènement, alors le thread de la boîte de dialogue sort son attente.
    Question: pourquoi utiliser des thread si l'API se fige pendant le calcul?

  4. #4
    Membre confirmé
    Inscrit en
    Janvier 2005
    Messages
    76
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Janvier 2005
    Messages : 76
    Par défaut
    En fait ma boite de dialogue créé un thread d'acquisition et un thread d'arret.

    Le thread d'arret scrute des valeurs pour déterminer quand arreter le thread d'acquisition. Le thread d'arret se termine quand il a fini de déterminer la fin de l'acquisition --> Il envoie alors un evenement au thread d'acquisition pour qu'il se termine.

    Lorsque l'acquisition est terminée, je lance le traitement par PostMessage à partir du thread d'acquisition. (mon traitement d'acquisition fait parti de mon processus d'acquisition, je l'ai décidé comme ça mais je vais surement devoir changé par rapport aux informations que tu m'as donné)

    Dans ma fonction de Traitement je réalise 3 processus : calcul, affichage, enregistrement.
    Ces processus sont appellés par PostMessage et doivent etre executé successivement et par simultanément.
    Pour cela j'ai utilisé les CEvent avec WaitForSingleObject infini, mais ça ne fonctionne pas car les processus ne sont pas executés.

    Donc maintenant il faut soit que je change ma méthode de traitement ...
    Soit que je créé d'autres Thread pour chaque processus.

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Donc, si je résume, cela se passe en deux parties:
    Phase 1/ Une boîte de dialogue, un thread d'acquisition et un thread de contrôle. La boîte ne fait rien, le thread d'acquisition récupère des données, le thread de contrôle vérifie que les données ne signalent pas la fin. (Attention à l'échange de ces données entre les deux threads). Quand le thread de contrôle trouve que l'acquisition doit se terminer, il envoie un message (SendMessage) ou signalise un évènement (SetEvent) pour dire au thread d'acquisition de se terminer. Le thread de contrôle se termine, le thread d'acquisition fait un PostMessage vers la boîte de dialogue et se termine.
    Phase 2/ La boîte de dialogue récupère les données acquises et à ce moment, elle doit dans l'ordre: traiter les données, mettre à jour l'affichage, enregistrer le résultat.
    Là où ton post est un peu confus, c'est que tu parle pour ta deuxième phase de processus (CreateProcess?).
    A mon avis, tu veux maintenir ce synchronisme mais pas figer ton appli (petite animation 'En traitement...'). Alors, une solution peut être:
    Ta boîte de dialogue lance l'animation (dans son thread), crée un thread de calcul. Pas d'attente spécifique (pas de WaitForSingleObject). Eventuellement, des menus/commandes sont à 'disable'.
    Lorsque ton thread de calcul se termine, il envoie un message à la boîte de dialogue pour lui indiquer la fin du traitement (PostMessage).
    Sur réception de ce message, la boîte de dialogue demande à l'IHM de se mettre à jour (appel de fonction? ou via message si IHM dans un autre thread - dans ce dernier cas, petite animation d'attente).
    Sur la fin de mise à jour (retour de fonction ou réception d'un PostMessage de l'IHM vers la boîte de dialogue), la boîte de dialogue créé un thread d'enregistrement et affiche une petite animation d'attente. Lorsque le thread d'enregistrement se termine, il envoie un PostMessage à la boîte de dialogue pour lui indiquer que le traitement est terminé.
    Ouf! c'est fini!

    En fait, je dirais:
    * si tu veux maintenir une IHM non figée: thread de travail<-> IHM communique par message (PostMessage). A toi de définir un protocole.
    * si tu veux synchroniser deux threads de travails (sans IHM), alors utilisation de CEvent pour les synchroniser.

  6. #6
    Membre confirmé
    Inscrit en
    Janvier 2005
    Messages
    76
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Janvier 2005
    Messages : 76
    Par défaut
    Dans un thread, lorsque qu'on poste un message (PostMessage), est ce que la fonction lié à ce message est executé dans le thread ou dans l'IHM ?

    Parce que si la fonction du PostMessage est executé dans l'IHM et pas dans le thread, je comprend vraiment pas pourquoi ça ne fonctionne pas !

    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
    void CDlg::OnCalculer(WPARAM wparam,LPARAM lparam)
    {
    	::SetEvent(m_hCalculEvent);
    }	
     
    void CDlg::OnTraitement(WPARAM wparam, LPARAM lparam)
    {
    	::PostMessage(this->m_hWnd, WM_CALCUL, 0, 0);
     
    	::WaitForSingleObject(m_hCalculEvent, INFINITE);
     
    	::CloseHandle(m_hCalculEvent);
    }
     
    UINT CDlg::ThreadAcquisition(LPVOID pvParam)
    {
    	CDlg  *pThis=reinterpret_cast< CDlg *>( pvParam) ;
    	srand(GetTickCount());
     
    	/* Du code pour la récupération des données*/
     
    	::PostMessage(pThis->m_hWnd, WM_TRAITEMENT, 0, 0);
     
    	/* Du code pour la gestion de la mémoire*/
     
    	return TRUE;
    }
    Le Message WM_TRAITEMENT est bien executer par la fonction OnTraitement.
    Par contre le Message WM_CALCUL en bien poster, mais la fonction OnCalculer n'est jamais appelé, c'est figé

  7. #7
    Membre confirmé
    Inscrit en
    Janvier 2005
    Messages
    76
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Janvier 2005
    Messages : 76
    Par défaut
    Je viens de trouver mon erreur ... du coup je me répond à moi-même.

    Dans mon Thread je poste un message WM_TRAITEMENT.
    Ce message est traité par l'IHM et pas par le thread.
    Dans la fonction de OnTraitement, je poste a nouveau un message WM_CALCUL et j'attends à l'infini un événement provenant de la fonction OnCalcul, le fait d'attendre l'evenement bloque l'IHM et donc l'execution du message !

    Donc la modification que j'ai effectué c'est du supprimer le message de traitement et de poster directement les messages de calculs, affichage ...

    en gros mon code est devenue comme celui là :
    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
    // Fonction executé dans l'IHM appelé par le message WM_CALCUL
    void CDlg::OnCalculer(WPARAM wparam,LPARAM lparam)
    {
    	::SetEvent(m_hCalculEvent);
    }	
     
    // Thread de travail
    UINT CDlg::ThreadAcquisition(LPVOID pvParam)
    {
    	CDlg  *pThis=reinterpret_cast< CDlg *>( pvParam) ;
    	srand(GetTickCount());
     
    	/* Du code pour la récupération des données*/
     
    	::PostMessage(this->m_hWnd, WM_CALCUL, 0, 0);
     
    	::WaitForSingleObject(m_hCalculEvent, INFINITE);
     
    	::CloseHandle(m_hCalculEvent);
     
    	/* Du code pour la gestion de la mémoire*/
     
    	return TRUE;
    }
    J'ai voulu décomposer mon code pour que ce soit plus clair a comprendre, et j'ai trop décomposé ...

  8. #8
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 394
    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 394
    Par défaut
    Es-tu sûr qu'un SendMessage() ne serait pas plus approprié pour ça ?
    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.

  9. #9
    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
    Citation Envoyé par Médinoc Voir le message
    Es-tu sûr qu'un SendMessage() ne serait pas plus approprié pour ça ?
    pour les boites de dialogues modales il vaut mieux éviter un sendmessage il y a des risques de blocage avec la pompe a messages du thread principal.

  10. #10
    Membre confirmé
    Inscrit en
    Janvier 2005
    Messages
    76
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Janvier 2005
    Messages : 76
    Par défaut
    Avec le SendMessage ça fonctionne aussi, mais comme j'ai d'autres threads qui tournent en meme temps, ça "pourrait" un jour poser problème, donc je préfère utiliser la file d'attente.

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

Discussions similaires

  1. [AC-2000] Bloquage entre 2 dates
    Par campello dans le forum VBA Access
    Réponses: 14
    Dernier message: 16/06/2009, 09h52
  2. Différences entre Delphi et Visual Basic ?
    Par Anonymous dans le forum Débats sur le développement - Le Best Of
    Réponses: 75
    Dernier message: 30/03/2009, 20h09
  3. Ambiguité entre CEvent MFC et CEvent ATL
    Par Gabrielly dans le forum MFC
    Réponses: 6
    Dernier message: 21/12/2006, 20h09
  4. [réseaux] Bench en Perl pour avoir le débit entre 2 pc
    Par Frich dans le forum Programmation et administration système
    Réponses: 4
    Dernier message: 22/05/2002, 17h22
  5. communication entre programmes
    Par jérôme dans le forum C
    Réponses: 12
    Dernier message: 16/04/2002, 08h05

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