pthread exit vs retour
j'ai une fonction pthread runner articulable définie comme suit:
void *sumOfProducts(void *param)
{
...
pthread_exit(0);
}
ce fil est supposé joindre le fil principal.
chaque fois que J'ai passé mon programme par Valgrind, J'ai eu le fuites suivantes :
LEAK SUMMARY:
definitely lost: 0 bytes in 0 blocks
indirectly lost: 0 bytes in 0 blocks
possibly lost: 0 bytes in 0 blocks
still reachable: 968 bytes in 5 blocks
suppressed: 0 bytes in 0 blocks
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 15 from 10)
j'ai vérifié la page de manuel pour pthreads qui a dit:
The new thread terminates in one of the following ways:
* It calls pthread_exit(3), specifying an exit status value that is
available to another thread in the same process that calls
pthread_join(3).
* It returns from start_routine(). This is equivalent to calling
pthread_exit(3) with the value supplied in the return statement.
* It is canceled (see pthread_cancel(3)).
* Any of the threads in the process calls exit(3), or the main thread
performs a return from main(). This causes the termination of all
threads in the process.
miraculeusement, quand j'ai remplacé le pthread_exit () par un retour déclaration, les fuites ont disparu .
return(NULL);
ma question actuelle est en trois volets:
- Quelqu'un peut-il expliquer pourquoi la déclaration de retour n'a donné lieu à aucune fuite?
- y a-t-il une différence fondamentale entre les deux énoncés, en ce qui concerne la sortie des fils?
- dans l'affirmative, à quel moment l'un devrait-il être préféré à l'autre?
5 réponses
le test minimal suivant montre le comportement que vous décrivez:
#include <pthread.h>
#include <unistd.h>
void *app1(void *x)
{
sleep(1);
pthread_exit(0);
}
int main()
{
pthread_t t1;
pthread_create(&t1, NULL, app1, NULL);
pthread_join(t1, NULL);
return 0;
}
valgrind --leak-check=full --show-reachable=yes
affiche 5 blocs alloués à partir de fonctions appelées par pthread_exit()
qui n'est pas créé mais est toujours accessible à la sortie du processus. Si le pthread_exit(0);
est remplacé par return 0;
, les 5 blocs ne sont pas attribués.
cependant, si vous testez la création et l'assemblage d'un grand nombre de threads, vous trouverez que la quantité de mémoire libre en usage à la sortie est-ce que n'augmente pas . Ceci, et le fait qu'il est toujours accessible, indique que vous voyez juste une bizarrerie de l'implémentation glibc. Plusieurs fonctions glibc allouent de la mémoire avec malloc()
la première fois qu'ils sont appelés, qu'ils gardent alloué pour le reste de la durée de vie du processus. glibc ne prend pas la peine de libérer cette mémoire à la sortie du processus, car il sait que le processus est en train d'être détruit de toute façon - ce serait juste un gaspillage de cycles CPU.
Je ne suis pas sûr que cela vous intéresse toujours, mais je suis en train de déboguer une situation similaire. Les Threads qui utilisent pthread_exit
font que valgrind déclare les blocs accessibles. La raison semble être assez bien expliquée ici:
https://bugzilla.redhat.com/show_bug.cgi?id=483821
Essentiellement, il semble pthread_exit
provoque un dlopen
qui n'est jamais nettoyé explicitement lorsque le processus s'arrête.
il semble que l'appel à exit() (et, apparemment, pthread_exit()) laisse les variables allouées automatiquement allouées. Vous devez soit retourner ou jeter afin de se détendre correctement.
Par C++ valgrind fuites possibles sur STL chaîne :
@Klaim: Je ne vois pas où ce document dit que je me trompe, mais si c'est le cas alors c'est mal. Pour citer la norme c++ (§18.3 / 8): "Automatique objets ne sont pas détruit suite à l'appel de exit()." – James McNellis Sep 10 ' 10 at 19: 11
depuis que faire un " return 0 "au lieu de" pthread_exit(0) " semble résoudre votre problème (et le mien.. merci), je suppose que le comportement est similaire entre les deux.
utilisez-vous vraiment C++, par hasard? Pour clarifier - votre fichier source se termine par une extension .c
, et vous le compilez avec gcc
, pas g++
?
il semble raisonnablement probable que votre fonction soit en train d'allouer des ressources que vous attendez d'être nettoyées automatiquement lorsque la fonction retourne. Les objets c++ locaux comme std::vector
ou std::string
font ceci, et leurs destructeurs ne seront probablement pas lancés si vous appelez pthread_exit
, mais serait nettoyé si vous venez de retour.
Ma préférence est d'éviter les API de bas niveau telles que pthread_exit
, et toujours simplement revenir de la fonction thread, lorsque cela est possible. Ils sont équivalents, sauf que pthread_exit
est une construction de contrôle de flux qui contourne le langage que vous utilisez, mais return
ne le fait pas.
j'ai l'expérience que valgrind a des difficultés à suivre le stockage est alloué pour l'état de recrutables threads. (Ceci va dans la même direction que celle indiquée par les FAC.)
Puisqu'il semble que vous retournez toujours une valeur de 0
je suppose que vous avez peut-être besoin de joindre vos fils du point de vue de l'application? Si donc envisager de les lancer détaché dès le début, cela évite l'allocation de la mémoire.
l'inconvénient est que vous avez soit:
- pour mettre en œuvre votre propre barrière à la
fin de votre
main
. Si vous connaissez le nombre de fils à l'avance, un répartition statique simplepthread_barrier
suffirait. - ou pour sortir de vous
main
avecpthread_exit
tels que vous ne tuer le reste des fils de course qui ne soit pas encore terminé.