systems - sistemas operativos tanenbaum 4 edicion pdf
pthread_exit vs. return (5)
¿Estás usando C ++, por casualidad? Para aclarar: ¿su archivo fuente termina con una extensión .c
, y está compilando con gcc
, no con g++
?
Parece razonablemente probable que su función esté asignando recursos que espera que se limpien automáticamente cuando la función retorna. Los objetos locales de C ++ como std::vector
o std::string
hacen esto, y sus destructores probablemente no se ejecutarán si llama a pthread_exit
, pero se limpiarían si acaba de regresar.
Mi preferencia es evitar las API de bajo nivel como pthread_exit
, y siempre regresar de la función de subprocesos, siempre que sea posible. Son equivalentes, excepto que pthread_exit
es una construcción de control de flujo de facto que elude el idioma que está utilizando, pero el return
no.
Tengo una función de corredor pthread unible que se define a continuación:
void *sumOfProducts(void *param)
{
...
pthread_exit(0);
}
Se supone que este hilo se une al hilo principal.
Cada vez que ejecutaba mi programa a través de Valgrind, obtenía las siguientes filtraciones :
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)
Revisé la página man para pthreads que decía:
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.
Milagrosamente, cuando reemplacé el pthread_exit () con una instrucción return, las filtraciones desaparecieron .
return(NULL);
Mi verdadera pregunta es triple:
- ¿Alguien puede explicar por qué la declaración de devolución no dio pérdidas?
- ¿Hay alguna diferencia fundamental entre ambas declaraciones, en relación con salir de los hilos?
- Si es así, ¿cuándo se debe preferir uno al otro?
El siguiente caso de prueba mínima exhibe el comportamiento que describe:
#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
muestra 5 bloques asignados desde las funciones llamadas por pthread_exit()
que no están abiertas, pero que aún se pueden alcanzar al salir del proceso. Si el pthread_exit(0);
es reemplazado por return 0;
, los 5 bloques no están asignados.
Sin embargo, si prueba creando y uniendo un gran número de subprocesos, encontrará que la cantidad de memoria no actualizada en uso al salir no aumenta. Esto, y el hecho de que todavía es alcanzable, indica que solo está viendo una rareza en la implementación de glibc. Varias funciones de glibc asignan memoria con malloc()
la primera vez que son llamadas, que se mantienen asignadas por el resto de la vida útil del proceso. glibc no se molesta en liberar esta memoria en la salida del proceso, ya que sabe que el proceso se está derribando de todos modos, sería simplemente un desperdicio de ciclos de CPU.
No estoy seguro de si todavía está interesado en esto, pero actualmente estoy depurando una situación similar. Los hilos que usan pthread_exit
causan que valgrind reporte bloques alcanzables. La razón parece estar bastante bien explicada aquí:
https://bugzilla.redhat.com/show_bug.cgi?id=483821
Básicamente, parece que pthread_exit
causa un dlopen
que nunca se limpia de forma explícita cuando el proceso finaliza.
Parece que llamar a exit () (y, aparentemente, pthread_exit ()) deja asignadas automáticamente las variables asignadas. Debe regresar o arrojar para relajarse correctamente.
Per C ++ valgrind posibles fugas en cadena STL :
@Klaim: No veo dónde dice ese documento que estoy equivocado, pero si lo hace, entonces está mal. Para citar el estándar de C ++ (§18.3 / 8): "Los objetos automáticos no se destruyen como resultado de llamar a exit ()". - James McNellis 10 de septiembre de 2010 a las 19:11
Dado que hacer un "return 0" en lugar de "pthread_exit (0)" pareció resolver tu problema (y el mío ... gracias), supongo que el comportamiento es similar entre los dos.
Tengo la experiencia de que valgrind tiene dificultades para rastrear el almacenamiento que se asigna para el estado de los hilos que se pueden unir. (Esto va en la misma dirección que indica caf).
Como parece que siempre devuelves un valor de 0
, supongo que tal vez necesites unir tus hilos desde el punto de vista de una aplicación. Si es así, considere lanzarlas desde el principio, esto evita la asignación de esa memoria.
El inconveniente es que o bien tienes:
- para implementar tu propia barrera al final de tu
main
. Si conoce el número de subprocesos de antemano, haría unapthread_barrier
asignada de forma estática simple. - o salir de
main
conpthread_exit
para no matar el resto de los hilos que aún no se terminaron.