Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1633 connectés 

  FORUM HardWare.fr
  Programmation
  C++

  Win32, threads, messages et blocages

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

Win32, threads, messages et blocages

n°1398558
slash33
Posté le 01-07-2006 à 14:30:32  profilanswer
 

Bonjour.
 
Je suis confronté à un cas d'inter-blocage de threads dans le cadre d'un projet professionnel (il faut donc trouver une solution assez vite).
 
Exposé du problème:
 
Le thread principal A crée un thread auxiliaire B au démarrage (en fait il en crée de multiples copies mais le fonctionnement est le même).
Le thread B fait appel à SendMessage(hWndOwnedByThreadA, ...) régulièrement.
Le thread A peut être amené à terminer PROPREMENT l'exécution du(es) thread(s) B. Pour cela, le thread A positionne un booléen à TRUE qui a pour effet de mettre fin à la boucle de traitement du thread B. Puis le ThreadA attend que le thread B se termine. Le problème : si le thread B est en en train d'exécuter un SendMessage, les deux threads sont morts !
 
En code ça donne (à peu près):
 

Code :
  1. --------- Thread A ---------------
  2. // ** création du thread B **
  3. ThreadBData *data = new ThreadBData;
  4. data->pTerminateThread = &m_bTerminateThread; // m_bTerminateThreads est à FALSE
  5. // handle de la fenêtre cible appartenant au thread A pour le SendMessage exécuté dans le thread B
  6. data->hWnd = hWndToSendMessageTo;
  7. // ... autres initialisations de data
  8. // Crée le thread et l'exécute dans la foulée (pas suspendu)
  9. HANDLE hThreadB = CreateThread(ThreadBProc, (PVOID)pData, ...)
  10. // ** Terminaison du thread B **
  11. m_bTerminateThread = TRUE;
  12. ::WaitForSingleObject(m_hThreadB, INFINITE); // blocage potientiel ici !!
  13. // ici, le thread B est terminé
  14. ---------- Thread B -----------
  15. UINT ThreadBProc(PVOID pParam)
  16. {
  17.    ThreadBData *pData = (ThreadBData*)pParam;
  18.    BOOL *pTerminateThread = pData->pTerminateThread;
  19.    HWND hWndTarget = pData->hWnd;
  20.    delete pData;
  21.    while (!(*pTerminateThread))
  22.    {
  23.       if (anEventOccured())
  24.       {
  25.           SendMessage(hWndTarget, WM_THE_EVENT, ...); // si le thread auquel appartient hWndTarget est en attente, je suis mort !
  26.       }
  27.       ::Sleep(50); // dors un peu ; pas besoin de stresser la machine
  28.    }
  29.    // fait les libérations d'usage avant la fin du thread
  30.    return 0;
  31. }


 
La solution d'ajouter un timeout ne me plait pas. Il est hors de question de tuer le thread : celà pourrait avoir des conséquences fâcheuses. Je pense qu'en utilisant des objets de synchronisation type Event, la problématique sera exactement la même : l'objet du blocage serait alors le point de synchronisation.
 
Autre détail important : on ne peut pas remplacer SendMessage par une autre instruction (type SendNotifyMessage, PostMessage, etc) car l'ordre de traitement des messages est primordial et en pratique l'ordre n'est plus respecté voir les messages sont interprétés différement (messages fantômes, codes de message erronés ou intervertis) !


Message édité par slash33 le 01-07-2006 à 14:48:40
mood
Publicité
Posté le 01-07-2006 à 14:30:32  profilanswer
 

n°1398681
bjone
Insert booze to continue
Posté le 02-07-2006 à 00:22:04  profilanswer
 

Citation :

Puis le ThreadA attend que le thread B se termine. Le problème : si le thread B est en en train d'exécuter un SendMessage, les deux threads sont morts !


 
Bloqués ou morts ?

n°1398769
slash33
Posté le 02-07-2006 à 12:18:42  profilanswer
 

bloqués : les threads existent toujours mais leur execution est suspendue.

n°1398774
jagstang
Pa Capona ಠ_ಠ
Posté le 02-07-2006 à 12:40:51  profilanswer
 

deadlock...
tu peux faire des messages asynchrones pour le send message ?


---------------
What if I were smiling and running into your arms? Would you see then what I see now?  
n°1398778
slash33
Posté le 02-07-2006 à 13:12:35  profilanswer
 

Si je suis certain qu'ils seront exécutés DANS L'ORDRE D'EMISSION oui, sinon non. Mais j'ai indiqué qu'en pratique des appels à PostMessage() font échouer la logique du programme.


Message édité par slash33 le 02-07-2006 à 13:12:51
n°1398781
jagstang
Pa Capona ಠ_ಠ
Posté le 02-07-2006 à 13:20:47  profilanswer
 

les messages asynchrones sont en principe effectué dans l'ordre d'émission... (venant d'un seul process)


---------------
What if I were smiling and running into your arms? Would you see then what I see now?  
n°1398783
slash33
Posté le 02-07-2006 à 13:42:05  profilanswer
 

Je le pensais aussi mais visiblement cela pose des désordres importants au traitement des messages. Le principe de fonctionnement du logiciel est de modifier l'aspect de l'interface graphique à la réception d'un message. La mise à jour de l'interface fait elle-même appel au mécanisme de files de messages. Mais je pense pas que cela pose un problème.


Message édité par slash33 le 02-07-2006 à 13:44:42
n°1398796
bjone
Insert booze to continue
Posté le 02-07-2006 à 14:25:25  profilanswer
 

déplaçe ton positionnement de booléen  de manière a ce que A continue de traiter la queue de messages quelque temps pour bien consommer tous les messages.

n°1398854
slash33
Posté le 02-07-2006 à 17:13:47  profilanswer
 

Tu proposes de positionner le booléen à TRUE le plus tôt possible avant le WaitForSingleObject c'est ça ?
 
En pratique, le code réel est court. Qu'est-ce qui garanti à coup sûr que A sera toujours en état de traiter les messages envoyés par B avant que celui-ci ne se termine ?

n°1398859
bjone
Insert booze to continue
Posté le 02-07-2006 à 17:33:33  profilanswer
 

je penses que tu as un blocage parceque tout ce qui est produit dans la queue de messages n'est pas consommé.

mood
Publicité
Posté le 02-07-2006 à 17:33:33  profilanswer
 

n°1398864
slash33
Posté le 02-07-2006 à 18:01:44  profilanswer
 

Ben oui cela s'explique simplement : le Thread A qui consomme les messages est mis en attente (suspendu) en attendant que le Thread B se termine. Mais le thread B peut produire un message à destination de A avant de se terminer. Hors avec cette implémentation, le message produit par B doit être consommé par A pour rendre la main à B après l'exécution de SendMessage. Bref SendMessage est bloquant tant que le message n'a pas été traité (cf MSDN).
 
Ce problème est connu dans la litterature Windows sous le terme "message deadlocks". Je cherche la solution pour éviter ce blocage sur un code que je viens de prendre en main et qui fait tourner une usine!!


Message édité par slash33 le 02-07-2006 à 18:02:52
n°1398866
Trap D
Posté le 02-07-2006 à 18:11:40  profilanswer
 

Tu as envisagé SendMessageTimeout ?

n°1398872
bjone
Insert booze to continue
Posté le 02-07-2006 à 18:23:54  profilanswer
 

bin mets nous le code de la boucle de message de A.

n°1398890
slash33
Posté le 02-07-2006 à 19:59:56  profilanswer
 

Trap D a écrit :

Tu as envisagé SendMessageTimeout ?


Oui effectivement. Cependant cette solution n'a pas été retenue par le groupe de dév (3/4 personnes)
 
Penses tu que je devrais défendre quand même cette solution ?


Message édité par slash33 le 02-07-2006 à 20:04:35
n°1398892
slash33
Posté le 02-07-2006 à 20:02:08  profilanswer
 

bjone a écrit :

bin mets nous le code de la boucle de message de A.


Impossible pour deux raisons:
- je n'ai pas le droit de diffuser le code en dehors de l'entreprise (d'ailleurs je ne l'ai pas sur moi  :o )
- parce que l'IHM est codée en MFC et que la boucle de message est donc gérée par le modèle MFC d'interception des messages.
 
Quel(s) élément(s) de compréhension apporterait la connaissance de ce code ? :(


Message édité par slash33 le 02-07-2006 à 20:03:40
n°1398899
bjone
Insert booze to continue
Posté le 02-07-2006 à 20:14:18  profilanswer
 

bin déjà savoir que c'est du MFC ça aide.
ça permet de réfléchir à la chose.

n°1398900
bjone
Insert booze to continue
Posté le 02-07-2006 à 20:17:38  profilanswer
 

tu as donc quoi, un .cpp pour la classe de l'application, des .cpp avec les classes des fenêtres, dans l'un de tout ça ou un .cpp a part, tu as tes routines de threads ?

n°1398902
bjone
Insert booze to continue
Posté le 02-07-2006 à 20:20:32  profilanswer
 

Code :
  1. // Crée le thread et l'exécute dans la foulée (pas suspendu)
  2. HANDLE hThreadB = CreateThread(ThreadBProc, (PVOID)pData, ...)


 
il se passe bien quelque chose entre les deux non ?
 

Code :
  1. // ** Terminaison du thread B **
  2. m_bTerminateThread = TRUE;
  3. ::WaitForSingleObject(m_hThreadB, INFINITE); // blocage potientiel ici !!


 
les threads B sont crées depuis le thread A ou le thread "principal" ?

n°1398933
Trap D
Posté le 02-07-2006 à 21:17:54  profilanswer
 

e ne suis pas du tout spécialiste de ce genre de développement, simplement en regardant la doc, j'ai vu cette possibilité justement pour résoudre le problème du "message deadlocks".
Maintenant, si tes collègues ont refusé cette solution, je me vois mal te la conseiller ne connaissant pas les tenants et aboutissants du problème que tu exposes.

n°1399334
breizhbugs
Posté le 03-07-2006 à 12:19:46  profilanswer
 

Salut,
Et si B envoyait un sendmessage a A avec un message perso lui disant qu'il a compris qu'il doit se terminer et que c'est son dernier message?
A comprendrait alors qu'il n'aurait plus de message de B et pourrait passer en WaitForsingleObject sur le thread de B?

n°1403038
slash33
Posté le 08-07-2006 à 20:40:43  profilanswer
 

bjone a écrit :

tu as donc quoi, un .cpp pour la classe de l'application, des .cpp avec les classes des fenêtres, dans l'un de tout ça ou un .cpp a part, tu as tes routines de threads ?


Oui à cela il faut ajouter que l'IHM est exécutée dans son propre thread et que les classes collaboratrices sont des DLL MFC qui s'exécutent dans des threads auxilaires distincts. J'appellerai ces dernières les classes ouvrières. Les classes ouvrières communiquent des notifications à l'IHM sous la forme de messages windows spécifiques.
 
Intérêt de la méthode : les classes ouvrières peuvent être utilisées sans l'IHM (qui n'est qu'un frontal) ET les notifications sont traitées par le thread de l'IHM (ce que le pattern Observer n'aurait pas permis par exemple). Ce détail est très important car le code de l'architecture MFC est très capricieux lorsqu'on utilise le multithread (problème de CWnd / HWND).


Message édité par slash33 le 08-07-2006 à 20:48:50
n°1403042
slash33
Posté le 08-07-2006 à 20:43:45  profilanswer
 

breizhbugs a écrit :

Salut,
Et si B envoyait un sendmessage a A avec un message perso lui disant qu'il a compris qu'il doit se terminer et que c'est son dernier message?
A comprendrait alors qu'il n'aurait plus de message de B et pourrait passer en WaitForsingleObject sur le thread de B?


Non car A est responsable du cycle de vie de B. B ne sait pas quand il doit s'arrêter. D'ailleurs en pratique, le thread B exécute perpétuellement le même scénario juqu'à ce que A lui demande de s'arrêter.

Message cité 1 fois
Message édité par slash33 le 08-07-2006 à 20:44:24
n°1403044
slash33
Posté le 08-07-2006 à 20:47:18  profilanswer
 

bjone a écrit :

il se passe bien quelque chose entre les deux non ?


Oui : l'IHM exécute les requêtes de l'utilisateur et traite les notifications des thread B (ils sont plusieurs en nombre indéfini)
 

bjone a écrit :

les threads B sont crées depuis le thread A ou le thread "principal" ?


Le thread A est le thread principal (de l'IHM donc). Les thread B sont des threads ouvriers qui peuvent communiquer avec l'IHM.
 
En fait, les thread B sont créés à l'initialisation de l'IHM (c'est pas tout à fait vrai mais on s'en fout ici) et sont libérés  à la fermeture de l'IHM.
 
Mes collègues ont trouvé une solution : ils tuent le processus et tous ces threads!!! :ouch: A noter que les thread B pilotent généralement des systèmes électronique actifs. :whistle:


Message édité par slash33 le 08-07-2006 à 20:53:09
n°1403188
breizhbugs
Posté le 09-07-2006 à 11:53:01  profilanswer
 

slash33 a écrit :

Non car A est responsable du cycle de vie de B. B ne sait pas quand il doit s'arrêter. D'ailleurs en pratique, le thread B exécute perpétuellement le même scénario juqu'à ce que A lui demande de s'arrêter.


Ben si:
B sait qu'il faut s'arreter quand A mets sa variable m_bTerminateThread a TRUE.
Dans ce cas, le code de B devient un truc du genre:

Code :
  1. ...
  2. while (!(*pTerminateThread))
  3. {
  4.      if (anEventOccured())
  5.      {
  6.           SendMessage(hWndTarget, WM_THE_EVENT, ...); // si le thread auquel appartient hWndTarget est en attente, je suis mort !
  7.      }
  8.      ::Sleep(50); // dors un peu ; pas besoin de stresser la machine
  9. }
  10. SendMessage(hWndTarget, WM_FINDEB, ...); // Avec en plus le handle de B au cas ou ils y a plusieurs thread B
  11. ...
  12. //Fin de B
  13. ...


 
Quand a A, la gestion des messages est un peu plus compliquées mais pas tant que ca:
1- Dans le gestionnaire du bouton "quitter", vous mettez m_bTerminateThread a TRUE  
puis vous lancer un timer "TCLOSE" qui doit boucler et c'est tout! (c'est a dire que A continu de traiter les messages!)
2- Dans le gestionnaire du message WM_FINDEB, vous recuperer le handle des B qui se ferme. Vous compter le nombre de B qui se ferme (vous enregistrer les handles de B dans un tableau)(On sait combien il y a de threads B puisque tu dis que c'est A qui les crees).
3- Dans le gestionnaire du timer "TCLOSE" (qui est appelé regulierement), vous attendez que tous les threads B soit fermer en utilisant le compteur du point 2. quand ils sont tous fermer, vous pouver a ce moment la faire un WaitForSingleObject sur les handle de B, qui sera passant puisque B ne peut plus envoyé de message apres celui ci par construction!). Lorsque tous les WaitForSingleObject seront passé, vous pouver fermer A!

Message cité 1 fois
Message édité par breizhbugs le 09-07-2006 à 11:56:30
n°1403207
slash33
Posté le 09-07-2006 à 12:48:53  profilanswer
 

breizhbugs a écrit :


Quand a A, la gestion des messages est un peu plus compliquées mais pas tant que ca:
1- Dans le gestionnaire du bouton "quitter", vous mettez m_bTerminateThread a TRUE  
puis vous lancer un timer "TCLOSE" qui doit boucler et c'est tout! (c'est a dire que A continu de traiter les messages!)
2- Dans le gestionnaire du message WM_FINDEB, vous recuperer le handle des B qui se ferme. Vous compter le nombre de B qui se ferme (vous enregistrer les handles de B dans un tableau)(On sait combien il y a de threads B puisque tu dis que c'est A qui les crees).
3- Dans le gestionnaire du timer "TCLOSE" (qui est appelé regulierement), vous attendez que tous les threads B soit fermer en utilisant le compteur du point 2. quand ils sont tous fermer, vous pouver a ce moment la faire un WaitForSingleObject sur les handle de B, qui sera passant puisque B ne peut plus envoyé de message apres celui ci par construction!). Lorsque tous les WaitForSingleObject seront passé, vous pouver fermer A!


Intéressant.
 
Cependant j'ai quelques questions et remarques:
- les threads B ne sont pas détruits uniquement à la fermeture de l'application, ils sont détruits et créés dynamiquement par l'utilisateur. Cependant le modèle doit être exploitable.
- que fait on si l'un des threads B ne se termine pas ?
- est il nécessaire de faire un WaitForSingleObject dans la phase 3


Message édité par slash33 le 09-07-2006 à 12:50:31
n°1403379
breizhbugs
Posté le 09-07-2006 à 20:09:40  profilanswer
 

Citation :

Intéressant.
 
Cependant j'ai quelques questions et remarques:
1- les threads B ne sont pas détruits uniquement à la fermeture de l'application, ils sont détruits et créés dynamiquement par l'utilisateur. Cependant le modèle doit être exploitable.
2- que fait on si l'un des threads B ne se termine pas ?
3- est il nécessaire de faire un WaitForSingleObject dans la phase 3


Re,
1- pour le 1, peu importe comment sont detruit les threads B car quand ils quittent la boucle while, ils envoient
un sendMessage(WM_FINDEB) a A ce qui lui permet de toujours savoir quels sont les threads B qui passe de l'etat "en vie" a "je termine"et donc A peu mettre a jour sa liste de B
 
2- A utilise le timer "TCLOSE", il suffit de controler si ce timer est appelée trop de fois ce qui veut dire
(aaahh but de Zidane sur Penalty a 6 minutes du debut du match :-) qu'un thread B bloque, dans ce cas on kill le thread B je suppose?
 
3- Ma foi j'en sais rien ,ca depend de votre programme mais je suppose que c'est plus propre.

n°1406843
slash33
Posté le 14-07-2006 à 11:45:28  profilanswer
 

Merci pour la solution. On en discute en ce moment mais vu le monde qu'il y a pendant les vacances, le dossier ne va pas avancer bien vite. :o


Message édité par slash33 le 14-07-2006 à 11:45:53
n°1406917
breizhbugs
Posté le 14-07-2006 à 14:20:44  profilanswer
 

:hello:  
Bossez bien alors et donne des nouvelles, histoire de voir si tes collegues apprécient ma suggestion.

mood
Publicité
Posté le   profilanswer
 


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  C++

  Win32, threads, messages et blocages

 

Sujets relatifs
Win32, winsock, timeout et GPRS sur WM5Probleme de synchronisation ecriture tubes / multi threads fork
ajouter une fenetre à une application win32 console ?THREADS - utilisation de pthread.h
Problèmes de variables communes à différents Threads[msgbox] comment faire des liens avec plusieurs messages d'alertes
Threads sur un serveur J2EE ? Thread en tant que Singleton?[win32] PropertySheet() et PSM_CHANGED ...
API win32 + logiciel réseau[win32] couleur entourant un bouton
Plus de sujets relatifs à : Win32, threads, messages et blocages


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR