Pregunta pthread_exit vs. return


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 obtendría el 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:

  1. ¿Alguien puede explicar por qué la declaración de devolución no dio pérdidas?
  2. ¿Hay alguna diferencia fundamental entre ambas declaraciones, en relación con salir de los hilos?
  3. Si es así, ¿cuándo se debe preferir uno al otro?

32
2017-10-02 06:41


origen


Respuestas:


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 funciones llamadas por pthread_exit() que no está abierto, pero todavía accesible en la salida del proceso. Si el pthread_exit(0); es reemplazado 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 al salir no incrementar. 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 reciben una llamada, que 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.


36
2017-10-02 07:03



No estoy seguro de si todavía está interesado en esto, pero actualmente estoy depurando una situación similar. Hilos que usan pthread_exit causa que valgrind reporte bloques alcanzables. La razón parece estar bastante bien explicada aquí:

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

Esencialmente parece pthread_exit causa un dlopen que nunca se limpia de forma explícita cuando el proceso finaliza.


11
2018-05-09 13:51



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 posibles fugas en cadena STL:

@Klaim: No veo dónde dice ese documento que estoy equivocado, pero si   entonces sí que 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.


1
2018-06-02 20:18



¿Estás usando C ++, por casualidad? Para aclarar: el archivo fuente termina con un .c extensión, y usted está compilando con gccno g++?

Parece razonablemente probable que su función esté asignando recursos que espera que se limpien automáticamente cuando la función retorna. Objetos locales de C ++ como std::vectoro std::string haz esto, y sus destructores probablemente no se ejecutarán si llamas pthread_exit, pero se limpiaría si vuelves.

Mi preferencia es evitar las API de bajo nivel como pthread_exit, y siempre acaba de regresar de la función de subprocesos, cuando sea posible. Son equivalentes, excepto que pthread_exit es una construcción de control de flujo de facto que elude el lenguaje que estás usando, pero return no.


0
2017-10-02 06:55



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 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:

  1. para implementar su propia barrera en el fin de tu main. Si sabes el número de hilos de antemano, un simple estáticamente asignado pthread_barrier haría.
  2. o para salir de ti main con pthread_exit de tal manera que no matar el resto de los hilos en ejecución eso puede que aún no haya terminado.

0
2017-10-02 07:57