2010-10-02 13 views
29

que tienen una función de corredor pthread acoplable se define de la siguiente manera:pthread_exit vs volver

void *sumOfProducts(void *param) 
{ 
... 
pthread_exit(0); 
} 

se supone que este hilo para unir el hilo principal.

Cada vez que me encontré con mi programa a través de Valgrind que tendría la siguientes fugas:

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 del manual de pthreads que decían:

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é la pthread_exit() con una declaración de devolución, , las fugas desaparecieron.

return(NULL); 

Mi pregunta real es triple:

  1. Puede alguien explicar por qué la instrucción de retorno no dio fugas?
  2. ¿Hay alguna diferencia fundamental entre ambos enunciados, en relación con salir de los hilos?
  3. En caso afirmativo, ¿cuándo se debe preferir uno sobre el otro?
+0

¿Realmente está usando C++? C++ usa el alcance para destruir objetos y el retorno "saldría" de ese ámbito mientras que pthread_exit no lo hará. –

+0

Lo siento pero nunca menciono C++ en ninguna parte de mi pregunta. Estoy haciendo todo en C a partir de ahora. –

+1

Sé que no lo mencionaste, pero fue una suposición, es por eso que pregunté. :) ¿Podría proporcionarnos una [prueba] completa (http://sscce.org/) [case] (http://www.xs4all.nl/~weegen/eelis/iso-c++/testcase.xhtml)? –

Respuesta

34

El caso siguiente prueba mínima muestra el comportamiento que describes:

#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 a las funciones llamadas por pthread_exit() que es unfreed pero todavía accesible en la salida del proceso. Si el pthread_exit(0); se reemplaza por return 0;, los 5 bloques no están asignados.

Sin embargo, si prueba la creación y la unión de un gran número de subprocesos, encontrará que la cantidad de memoria no actualizada en uso en la salida 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 glibc asignan memoria con malloc() la primera vez que se invocan, que se guardan durante 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.

+0

¿No hay una forma de forzar la memoria libre asignada por glibc? No es que sea cierto, pero creo que lo he visto mencionar en algún lugar de este sitio ... – Christoffer

+2

@Christoffer: Valgrind, desde aproximadamente la versión 1.1, llama a una función llamada '__libc_freeres' que se supone que hace esto. Sin embargo, hay algunas versiones de glibc que tienen errores en esta función (ya que generalmente no se llama a menos que el programa se ejecute bajo un depurador de memoria). Por lo general, se llama por defecto, a menos que valgrind se ejecute con el argumento '--run-libc-freeres = no'. – Doug

+0

¡Gracias! Esto me estaba volviendo loco. Pensé que era una rareza glibc, pero no estaba seguro. – William

0

¿Está realmente usando C++, por casualidad? Para aclarar: ¿su archivo de origen finaliza con una extensión .c y lo 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 al 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 return no.

+0

Estoy utilizando solo C. –

+0

y está compilando con 'gcc', y no hay posibilidad de que ni él ni ningún código que invoque pueda estar usando accidentalmente las características de C++. – Doug

+0

Tenga la seguridad, ESTOY compilando con gcc. Por favor mira las etiquetas, antes de contestar. –

0

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.)

Dado que parece que siempre devuelve un valor de 0, ¿cree que quizás necesite unir sus 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 tiene:

  1. para implementar su propia barrera al final de su main. Si conoce el número de hilos de antemano, un simple asignado estáticamente pthread_barrier haría.
  2. o para salir con usted main pthread_exit de tal manera que usted no matar al resto de los subprocesos que se ejecutan que aún no podría ser terminado.
10

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 hacen que valgrind reporte bloques alcanzables. La razón parece estar bastante bien explicado aquí:

https://bugzilla.redhat.com/show_bug.cgi?id=483821

En esencia parece pthread_exit provoca una dlopen que nunca se limpian de forma explícita cuando el proceso finalice.

0

Parece que llamar a exit() (y, aparentemente, pthread_exit()) deja asignadas automáticamente las variables asignadas. Debe regresar o arrojar para relajarse correctamente.

por C++ valgrind possible leaks on STL string:

@Klaim: No veo en ese documento dice que estoy equivocado, pero si lo hace, entonces que es un error. 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 Sep 10 '10 a las 19:11

Desde hace un "retorno 0" en lugar de "pthread_exit (0)" parecido a resolver su problema (y la mía .. gracias), estoy suponiendo que el comportamiento es similar entre los dos.

Cuestiones relacionadas