¿Está bien llamar a pthread_exit desde main?

6 minutos de lectura

cuando llamo pthread_exit desde main, el programa nunca llega a terminar. Esperaba que el programa terminara, ya que estaba saliendo del único hilo del programa, pero no funciona. Parece colgado.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

int main(int argc, char *argv[])
{
    printf("-one-\n");

    pthread_exit(NULL);

    printf("-two-\n");
}

Explorador de procesos muestra que el (único) hilo está en Wait:DelayExecution estado.

De acuerdo a pthread_exit documentación:

El proceso finalizará con un estado de salida de 0 después de que se haya terminado el último subproceso. El comportamiento será como si la implementación llamara a exit() con un argumento cero en el momento de la finalización del subproceso.

Estoy usando Dev-C++ v4.9.9.2 y pthreads-win32 v2.8.0.0 (enlace contra libpthreadGC2.a).

La biblioteca parece estar bien (por ejemplo, llamando pthread_self o pthread_create desde main funciona bien).

¿Hay alguna razón por la que se supone que no debo llamar pthread_exit desde main?

  • ¿Por qué no return 0; en lugar de pthread_exit(NULL);?

    – En silicio

    24 de agosto de 2010 a las 17:57


  • Sé que yo pudo return o exit. Solo quiero saber si es legal terminar el hilo principal llamando pthread_exit.

    – usuario429788

    24 de agosto de 2010 a las 18:26

  • Volver desde main() es muy diferente a ejecutar pthread_exit(). El último permitirá que el resto de subprocesos vivos finalicen y luego saldrá con el valor de retorno 0. El primero terminará todo de inmediato.

    – Raúl Salinas Monteagudo

    15/04/2015 a las 11:45


avatar de usuario
torak

Bueno, definitivamente es legal en la implementación de linux de pthreads, consulte la sección de notas en pthreads_exit. Afirma

Para permitir que otros subprocesos continúen con la ejecución, el subproceso principal debe terminar llamando a pthread_exit() en lugar de exit(3).

Además, un vistazo al código fuente aquí (hacia el final) muestra que se traduce aproximadamente como _endthread o _endthreadex. La documentación aquí para aquellos que no mencionan no llamarlo en el hilo inicial.

  • Entonces supongo que debería ser legal en la implementación de win32 (sourceware.org/pthreads-win32/bugs.html) también. He estado buscando un error conocido que explique este comportamiento, pero no pude encontrarlo. En mi opinión, este es un comportamiento defectuoso o hay una razón real por la que se supone que no debo llamar pthread_exit en pthreads_win32. ¿Alguien puede confirmar alguna de estas hipótesis?

    – usuario429788

    24 de agosto de 2010 a las 18:52


  • @matasierra: He agregado algunos detalles más a la respuesta anterior. Además, ¿qué es lo que realmente ejecuta main?

    – torak

    24 de agosto de 2010 a las 19:07

  • El primero printf en realidad está impreso, pero el segundo no (como se esperaba). El problema es que el programa no terminará. Simplemente se vuelve un poco congelado.

    – usuario429788

    24 de agosto de 2010 a las 19:25

  • Finalmente encontré el problema. Creo que fue algún tipo de malware en mi sistema operativo. Acabo de limpiarlo con una herramienta antispyware y ahora funciona como se esperaba. Oh, mi… Me siento realmente estúpido. @torak Voy a aceptar tu respuesta. ¡Gracias por tu ayuda!

    – usuario429788

    24 de agosto de 2010 a las 19:45

Este comportamiento completamente legal y intencionado. Todo el proceso solo termina cuando todos los subprocesos terminan o exit se llama explícita o implícitamente.

Un regreso normal de main es equivalente a una llamada a exit. si terminas main con pthread_exit estás diciendo explícitamente que quieres que los otros hilos continúen.

avatar de usuario
Preeti

Está bien usar pthread_exit en main. Cuando se usa pthread_exit, el subproceso principal dejará de ejecutarse y permanecerá en estado zombi (difunto) hasta que todos los demás subprocesos salgan.

Si está utilizando pthread_exit en el subproceso principal, no puede obtener el estado de retorno de otros subprocesos y no puede realizar la limpieza de otros subprocesos (se puede realizar mediante pthread_join(3)). Además, es mejor separar subprocesos (pthread_detach(3)) para que los recursos de subprocesos se liberen automáticamente al finalizar el subproceso. Los recursos compartidos no se liberarán hasta que finalicen todos los subprocesos.

Está bien usarlo cuando no se asignan recursos en el subproceso principal, no se necesita limpieza. El siguiente código muestra el uso de pthread_exit en el hilo principal. El segundo printf en main no se imprime ya que el hilo principal sale después de llamar a pthread_exit. La salida de Ps muestra el subproceso principal extinto.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>

void *functionC(void *);

int main()
{
        int rc;
        pthread_t th;

        if(rc = pthread_create(&th, NULL, &functionC, NULL))
        {
                printf("Thread creation failed, return code %d, errno %d", rc,                 errno);
        }

        printf("Main thread %lu: Sleeping for 20 seconds\n", pthread_self());
        fflush(stdout);
        sleep(20);
        pthread_exit(NULL);
        printf("Main thread %lu: This will not be printed as we already called         pthread_exit\n", pthread_self());
        exit(0);
}

void *functionC(void *)
{
        printf("Thread %lu: Sleeping for 20 second\n", pthread_self());
        sleep(20);
        printf("Thread %lu: Came out of first and sleeping again\n", pthread_self());
        sleep(20);
        printf("CThread %lu: Came out of second sleep\n", pthread_self());
}

Salida del código anterior:

Main thread 140166909204288: Sleeping for 20 seconds
Thread 140166900684544: Sleeping for 20 second
Thread 140166900684544: Came out of first and sleeping again
CThread 140166900684544: Came out of second sleep

ps producción:

[email protected]:~/pthread_tst# ps -elfT |grep a.out
0 S root      9530  9530  9496  0  80   0 -  3722 hrtime 17:31 pts/1    00:00:00 ./a.out
1 S root      9530  9531  9496  0  80   0 -  3722 hrtime 17:31 pts/1    00:00:00 ./a.out
0 S root      9537  9537  2182  0  80   0 -  5384 pipe_w 17:31 pts/0    00:00:00 grep --color=auto a.out

[email protected]:~/pthread_tst# ps -elfT |grep a.out
0 Z root      9530  9530  9496  0  80   0 -     0 -      17:31 pts/1    00:00:00 [a.out] <defunct>
1 S root      9530  9531  9496  0  80   0 -  4258 hrtime 17:31 pts/1    00:00:00 ./a.out
0 S root      9539  9539  2182  0  80   0 -  5384 pipe_w 17:31 pts/0    00:00:00 grep     --color=auto a.out`

Por favor revise el blog Tecnología Fácil para obtener más información sobre los hilos.

avatar de usuario
karsten

Al probar en Linux (CentOS Linux versión 7.2.1511 (Core)), descubrí que, de hecho, el programa principal espera que continúen los subprocesos “secundarios”. Además, no pude pasar un código de retorno desde main, aunque se puede especificar como argumento para pthread_exit(), como dijo Raul arriba, siempre regresa con el código de salida 0:

retval=3;
pthread_exit(&retval);

También observamos un mensaje de error al usar el compilador Clang (versión 3.4.2) y las opciones de desinfección:

==5811==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: 0x7f4c090321d0 in thread T0
#0 0x7f4c08be3e29 in __interceptor_free (/home/karstenburger/tests/libc/pthread_exit_in_main/a+0x65e29)
#1 0x7f4c08333358 in free_key_mem (/lib64/libdl.so.2+0x1358)
#2 0x7f4c08745bc1 in __nptl_deallocate_tsd (/lib64/libpthread.so.0+0x7bc1)
#3 0x7f4c07771b38 in __libc_start_main (/lib64/libc.so.6+0x21b38)
#4 0x7f4c08bfa08c in _start (/home/karstenburger/tests/libc/pthread_exit_in_main/a+0x7c08c)

AddressSanitizer can not describe address in more detail (wild memory access suspected).
SUMMARY: AddressSanitizer: bad-free ??:0 __interceptor_free
==5811==ABORTING

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad