61 votes

Comment interroger un pthread pour savoir s'il est toujours en cours d'exécution ?

Dans mon destructeur, je veux détruire un thread proprement.

Mon objectif est d'attendre la fin de l'exécution d'un thread puis de le détruire.

La seule chose que j'ai trouvée sur l'interrogation de l'état d'un pthread est pthread_attr_setdetachstate mais cela vous indique seulement si votre fil l'est :

  • PTHREAD_CREATE_DETACHED
  • PTHREAD_CREATE_JOINABLE

Ces deux éléments n'ont rien à voir avec le fait que le fil de discussion soit toujours en cours ou non.

Comment interroger un pthread pour savoir s'il est toujours en cours d'exécution ?

0 votes

2 votes

C'est vieux de 10 ans, mais c'est parti... que voulez-vous dire par "attendre qu'un thread finisse de s'exécuter et ensuite détruire le thread" ? Quand un thread finit de s'exécuter, il est détruit.

46voto

jeremytrimble Points 562

On dirait que vous avez deux questions ici :

Comment puis-je attendre que mon fil se termine ?

Réponse : Ceci est directement supporté par pthreads -- rendez votre thread à arrêter JOINABLE (quand il est démarré pour la première fois), et utilisez pthread_join() pour bloquer votre thread actuel jusqu'à ce que le thread à arrêter ne tourne plus.


Comment puis-je savoir si mon fil est toujours en cours ?

Réponse : Vous pouvez ajouter un drapeau "thread_complete" pour faire l'affaire :

Scénario : Le fil A veut savoir si le fil B est encore en vie.

Lorsque le thread B est créé, il reçoit un pointeur vers l'adresse du drapeau "thread_complete". L'indicateur "thread_complete" doit être initialisé à NOT_COMPLETED avant la création du thread. La fonction du point d'entrée du thread B doit immédiatement appeler pthread_cleanup_push() pour pousser un "cleanup handler" qui met l'indicateur "thread_complete" à COMPLETED.

Pour plus de détails sur les gestionnaires de nettoyage, cliquez ici : Gestionnaire de nettoyage de pthread

Vous devrez inclure un appel correspondant à pthread_cleanup_pop(1) pour vous assurer que le gestionnaire de nettoyage sera appelé quoi qu'il arrive (c'est-à-dire si le thread sort normalement OU à cause d'une annulation, etc.)

Ensuite, le fil A peut simplement vérifier l'indicateur "thread_complete" pour voir si le fil B est déjà sorti.

NOTE : Votre drapeau "thread_complete" doit être déclaré "volatile" et doit être un type atomique -- les compilateurs GNU fournissent le sig_atomic_t dans ce but. Cela permet aux deux threads d'accéder de manière cohérente aux mêmes données sans avoir besoin de constructions de synchronisation (mutex/semaphores).

4 votes

Les gestionnaires de nettoyage sont totalement inutiles, sauf si vous utilisez l'annulation (qui est généralement un outil plutôt dangereux). De plus, vous pouvez utiliser une variable de condition pthread avec la variable flag pour permettre d'attendre sur celle-ci sans tourner et brûler des cycles de processeur.

0 votes

Si l'objectif est de savoir s'il faut ou non appeler join pour "détruire" un handle pthread_t, vérifier un drapeau ne fonctionnera pas car le thread peut ne pas s'être terminé lorsque vous vérifiez le drapeau mais réussir à se terminer avant d'être joint (notez que joindre un thread qui a été joint entraîne un comportement indéfini, et laisser un thread joignable non joint entraîne une fuite).

0 votes

A tous ceux qui veulent utiliser la solution mentionnée ci-dessus, j'ai répondu ci-dessous avec un problème avec cette solution. Lisez-le avant de l'appliquer à votre solution. Ce n'est peut-être pas un problème dans de nombreux cas, mais vous voudrez peut-être le savoir.

28voto

pthread_kill Points 221
pthread_kill(tid, 0);

Aucun signal n'est envoyé, mais le contrôle d'erreur est toujours effectué et vous pouvez l'utiliser pour vérifier l'existence de tid. l'existence de tid.

ATTENTION : Cette réponse est incorrecte. La norme interdit spécifiquement de transmettre l'ID d'un thread dont la durée de vie est terminée. Cet ID pourrait maintenant spécifier un autre thread ou, pire, il pourrait se référer à une mémoire qui a été libérée, provoquant un crash.

2 votes

Pour mon problème, c'était exactement ce dont j'avais besoin pour le résoudre, merci. J'ai utilisé (pthread_kill(tid, 0) != ESRCH).

2 votes

Veuillez noter ceci : stackoverflow.com/questions/1693180/

1 votes

@DavidSchwartz Dans de tels cas, il est préférable d'indiquer clairement que votre modification ne fait pas partie de la réponse originale, comme suggéré. aquí .

10voto

Jeremy Friesner Points 16684

Je pense que tout ce dont vous avez besoin est d'appeler pthread_join(). Cet appel ne reviendra pas tant que le thread ne sera pas sorti.

Si vous voulez seulement vérifier si le thread est toujours en cours d'exécution ou non (et notez que ce n'est généralement pas ce que vous devriez vouloir faire !), vous pourriez faire en sorte que le thread mette un booléen volatile à false juste avant de sortir... alors votre main-thread pourrait lire le booléen et s'il est toujours vrai, vous savez que le thread est toujours en cours d'exécution. (s'il est faux, d'un autre côté, vous savez que le thread est au moins presque parti ; il peut encore exécuter le code de nettoyage qui se produit après avoir mis le booléen à false, cependant, donc même dans ce cas vous devriez toujours appeler pthread_join avant d'essayer de libérer toutes les ressources auxquelles le thread pourrait avoir accès)

0 votes

+1 pour avoir expliqué la condition de course potentielle impliquée et une solution simple à celle-ci.

2 votes

"alors votre main-thread pourrait lire le booléen et si c'est toujours vrai, vous savez que le thread est toujours en cours d'exécution." -- Dans ce cas, vous sauriez seulement si le thread est sorti proprement ou non, ce qui n'est pas la même chose que de savoir s'il est toujours en cours d'exécution ou non.

2 votes

Hmm, j'ai probablement raté une possibilité ici, mais il semble que le thread est soit toujours en cours d'exécution, soit il a quitté, soit il s'est écrasé. Et si le thread s'est écrasé, il a emporté le reste du processus avec lui, donc il n'y a pas de second thread encore présent pour le détecter de toute façon.

6voto

Dewfy Points 11277

Il n'y a pas de solution entièrement portable, regardez si votre plateforme supporte pthread_tryjoin_np ou pthread_timedjoin_np. Ainsi, il suffit de vérifier si le thread peut être joint (bien sûr, il a été créé avec PTHREAD_CREATE_JOINABLE).

1 votes

Ces interfaces ont été rejetées par POSIX, et ne devraient pas être utilisées dans des applications qui se veulent portables. La même chose peut être accomplie de manière portable avec une variable de condition.

1 votes

Ce n'est pas parce que la même chose peut être accomplie avec une variable de condition que le pthread_tryjoin_np n'est pas une interface plus propre.

0 votes

J'utilisais une variable de condition - à la fin du fil, juste avant return NULL; J'ai effacé la variable. Mais mon système à faible mémoire RAM se plantait à cause d'un trop grand nombre de threads non terminés. La cause profonde n'est pas claire. Mais ce qui est clair, c'est que cette variable ne peut pas être utilisée comme indicateur de sortie de thread.

4voto

n3whous3 Points 180

Permettez-moi de faire une remarque sur la réponse "gagnante", qui comporte un énorme défaut caché et qui, dans certains contextes, peut entraîner des plantages. À moins que vous n'utilisiez pthread_join, elle reviendra sans cesse. Supposons que vous ayez un processus et une bibliothèque partagée. Appelez la bibliothèque lib.so.

  1. Tu l'ouvres, tu commences un fil dedans. Supposons que vous ne voulez pas qu'il s'y joigne, alors vous le rendez détachable.
  2. Le processus et la logique de la librairie partagée font leur travail, etc...
  3. Vous voulez vider lib.so, car vous n'en avez plus besoin.
  4. Vous appelez un shutdown sur le thread et vous dites, que vous voulez lire un drapeau après le thread de votre lib.so, qu'il a terminé.
  5. Vous continuez sur un autre fil avec dlclose, parce que vous voyez, que vous avez vu, que le drapeau montre maintenant le fil comme "terminé".
  6. dlclose va charger toute la pile et la mémoire liée au code.
  7. Oups, mais dlclose n'arrête pas les fils. Et vous savez, même quand vous êtes à la dernière ligne du cleanup handler pour mettre la variable volatile atomic flag "thread is finished", vous devez toujours revenir de beaucoup de méthodes sur la pile, en redonnant des valeurs, etc. Si une énorme priorité de thread a été donnée au thread de #5+#6, vous recevrez dlclose avant de pouvoir VRAIMENT vous arrêter sur le thread. Vous aurez parfois de beaux crashs.

Permettez-moi de préciser qu'il ne s'agit pas d'un problème hypothétique, j'ai eu le même problème sur notre projet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X