2008-11-21 9 views
1

J'ai un thread qui, lorsque sa fonction sort de sa boucle (la sortie est déclenchée par un événement), effectue un nettoyage, puis définit un événement différent pour faire savoir à un thread maître que c'est fait.Windows: Dans quelles circonstances SetEvent() ne peut-il pas retourner immédiatement?

Cependant, dans certaines circonstances, SetEvent() ne semble pas revenir après définit « je suis fait » l'événement du fil.

Ce thread fait partie d'une DLL et le problème semble se produire après que la DLL a été chargée/attachée, le thread démarré, le thread terminé et la DLL détachée/déchargée un certain nombre de fois sans arrêt de l'application . Le nombre de fois que cette séquence doit être répétée avant que ce problème n'arrive est variable.

Dans le cas où vous êtes sceptique que je sais de quoi je parle, j'ai déterminé ce qui se passe en encadrant l'appel SetEvent() avec des appels à OutputDebugString(). La sortie avant SetEvent() apparaît. Ensuite, le thread en attente produit une sortie qui indique que l'événement a été défini.

Cependant, le deuxième appel à OutputDebugString() dans le thread sortant (celui AFTER SetEvent()) ne se produit jamais, ou au moins sa chaîne n'apparaît jamais. Si cela se produit, l'application se bloque quelques instants plus tard.

(Notez que les appels à OutputDebugString() ont été ajoutés après que le problème a commencé à se produire, il est donc peu susceptible d'être accroché là, plutôt que dans SetEvent().)

Je ne suis pas tout à fait sûr ce qui cause la crash, mais cela se produit dans le même thread dans lequel SetEvent() n'est pas retourné immédiatement (j'ai suivi/sortie les identifiants de threads). Je suppose qu'il est possible que SetEvent() retourne finalement, par quel point le contexte auquel il retourne est parti/invalide, mais qu'est-ce qui pourrait causer un tel retard?

Il se trouve que je suis aveuglé en regardant ce code si longtemps, et il n'y a pas eu même à moi de vérifier le code de retour. J'ai fini de le regarder pour aujourd'hui, donc je vais savoir ce qu'il retourne (si il revient) le lundi et je vais modifier cette question avec cette info alors.

Mise à jour: J'ai changé le code (maître) pour attendre le fil pour sortir plutôt que pour elle pour définir l'événement, et retiré l'appel du thread esclave SetEvent(). Cela a changé la nature du bogue: maintenant, au lieu de ne pas revenir de SetEvent(), il ne quitte pas le thread et tout se bloque.

Ceci indique que le problème n'est pas avec SetEvent(), mais quelque chose de plus profond. Aucune idée de quoi, pourtant, mais c'est bon de ne pas courir après cette impasse.

Mise à jour (février 13/09):
Il est avéré que le problème était plus profond que ce que je pensais quand je posé cette question. jdigital (et probablement d'autres) a à peu près cloué le problème sous-jacent: nous essayions de décharger un thread dans le cadre du processus de détachement d'une DLL. Ceci, comme je ne l'avais pas réalisé à l'époque, mais que j'ai depuis découvert par la recherche ici et ailleurs (le blog de Raymond Chen, par exemple), est une très mauvaise chose. Le problème était, en raison de la manière dont il était codé et de son comportement, pas évident que c'était le problème sous-jacent - il était camouflé comme toutes sortes d'autres mauvais comportements que je devais traverser.

Certaines des suggestions ici m'a aidé à le faire, alors je suis reconnaissant à tous ceux qui ont contribué. Je vous remercie!

+0

ne fonctionnent pas sur le disque .... :-) – Asher

Répondre

1

Qui décharge la DLL et à quel moment le déchargement est-il effectué? Je me demande s'il y a un problème de synchronisation ici où la DLL est déchargée avant que le fil a couru à la fin.

2

Etes-vous en train de dereferncing un HANDLE * pour passer à SetEvent? Il est plus probable que la référence du gestionnaire d'événements ne soit pas valide et que le blocage soit une violation d'accès (c'est-à-dire, l'accès à la mémoire non autorisée).

0

Vous pouvez utiliser WinDbg pour intercepter le plantage et examiner la pile.

0

Pourquoi avez-vous besoin de définir un événement dans le thread esclave pour déclencher le thread principal que le thread est fait? juste sortir le fil, le fil maître d'appel doit attendre que le thread de travail pour sortir, par exemple le code de pseudo -

Master 
{ 
    TerminateEvent = CreateEvent (...) ; 
    ThreadHandle = BeginThread (Slave, (LPVOID) TerminateEvent) ; 
    ... 
    Do some work 
    ... 
    SetEvent (TerminateEvent) ; 
    WaitForSingleObject (ThreadHandle, SOME_TIME_OUT) ; 
    CloseHandle (TerminateEvent) ; 
    CloseHandle (ThreadHandle) ; 
} 

Slave (LPVOID ThreadParam) 
{ 
    TerminateEvent = (HANDLE) ThreadParam ; 
    while (WaitForSingleObject (TerminateEvent, SOME__SHORT_TIME_OUT) == WAIT_TIMEOUT) 
    { 
     ... 
     Do some work 
     ... 
    } 
} 

Il y a beaucoup de conditions d'erreur et les États à vérifier mais est l'essence même de la façon dont je le fais normalement il.

Si vous pouvez vous en procurer, prenez ce livre, ça a changé ma vie en ce qui concerne le développement de Windows quand je l'ai lu il y a de nombreuses années.

Advanced Windows: The Developer's Guide to the Win32 Api for Windows Nt 3.5 and Windows 95 (Paperback), by Jeffrey Richter (Author)

Questions connexes