¿Cómo consulta un pthread para ver si todavía se está ejecutando?

11 minutos de lectura

En mi destructor quiero destruir un hilo limpiamente.

Mi objetivo es esperar a que un hilo termine de ejecutarse y LUEGO destruir el hilo.

Lo único que encontré sobre consultar el estado de un pthread es pthread_attr_setdetachstate pero esto solo te dice si tu hilo es:

  • PTHREAD_CREATE_DETACHED
  • PTHREAD_CREATE_JOINABLE

Ambos no tienen nada que ver con si el hilo aún se está ejecutando o no.

¿Cómo consulta un pthread para ver si todavía se está ejecutando?

  • Consulte también stackoverflow.com/questions/1693180/…

    – usuario470379

    13 de enero de 2011 a las 21:15

  • 10 años, pero aquí va… ¿qué quieres decir con “esperar a que un subproceso termine de ejecutarse y luego destruir el subproceso”? Cuando un hilo termina de ejecutarse, se destruye.

    – papajonia

    12 de mayo de 2020 a las 8:41

Parece que tienes dos preguntas aquí:

¿Cómo puedo esperar hasta que se complete mi hilo?

Respuesta: Esto es compatible directamente con pthreads: haga que su hilo que se detendrá sea UNIABLE (cuando se inicie por primera vez) y use pthread_join() para bloquear su hilo actual hasta que el hilo que se detendrá ya no esté corriendo.


¿Cómo puedo saber si mi hilo todavía se está ejecutando?

Respuesta: puede agregar un indicador “thread_complete” para hacer el truco:

Escenario: el subproceso A quiere saber si el subproceso B todavía está vivo.

Cuando se crea el subproceso B, se le asigna un puntero a la dirección del indicador “thread_complete”. El indicador “thread_complete” debe inicializarse en NOT_COMPLETED antes de crear el hilo. La función de punto de entrada del subproceso B debe llamar inmediatamente a pthread_cleanup_push() para enviar un “controlador de limpieza” que establece el indicador “thread_complete” en COMPLETADO.

Vea los detalles sobre los controladores de limpieza aquí: controladores de limpieza pthread

Querrá incluir una llamada pthread_cleanup_pop(1) correspondiente para asegurarse de que se llame al controlador de limpieza sin importar qué (es decir, si el hilo sale normalmente O debido a una cancelación, etc.).

Luego, el subproceso A puede simplemente verificar el indicador “thread_complete” para ver si el subproceso B ya salió.

NOTA: Su indicador “thread_complete” debe declararse “volátil” y debe ser de tipo atómico; los compiladores de GNU proporcionan sig_atomic_t para este propósito. Esto permite que los dos subprocesos accedan consistentemente a los mismos datos sin necesidad de construcciones de sincronización (mutexes/semáforos).

  • Los controladores de limpieza son completamente innecesarios a menos que utilice la cancelación (que generalmente es una herramienta bastante peligrosa). Además, es posible que desee utilizar una variable de condición pthread junto con la variable flag para habilitar la espera sin girar ni quemar ciclos de CPU.

    – R.. GitHub DEJA DE AYUDAR A ICE

    13 de enero de 2011 a las 1:09

  • Si el objetivo es saber si llamar o no a join para “destruir” un identificador pthread_t, verificar un indicador no funcionará porque es posible que el subproceso no haya terminado cuando marque el indicador, pero aún logra terminar antes de unirse (nota que unirse a un hilo que se ha unido da como resultado un comportamiento indefinido, y dejar un hilo que se puede unir sin unir da como resultado una fuga).

    – Emilio

    29 de septiembre de 2015 a las 5:28

  • A todos los que quieran usar la solución antes mencionada, les respondí a continuación con un problema con esta solución. Léalo antes de aplicarlo a su solución. Puede que no sea un problema en muchos casos, pero es posible que desee saberlo.

    – casa nueva

    26/09/2016 a las 11:25

  • @Emil No hay problema. Solo asegúrese de llamar a unirse una vez y solo una vez. La carrera es inofensiva: sí, su unión podría bloquearse por una pequeña fracción de segundo si verifica la bandera después de que se establece y antes de que termine el hilo, pero el hilo está a punto de terminar de todos modos.

    –David Schwartz

    26/09/2016 a las 11:30

  • En los casos en que calcula algo y luego señala thread_completepor lo que se puede utilizar el resultado del cálculo, esto es incorrecto. volatile sig_atomic_t todavía se puede reordenar, por lo que podría escribirse antes de que el resultado del cálculo sea visible y los consumidores puedan ver un estado no válido.

    – a3f

    4 mayo 2019 a las 15:26

avatar de usuario
pthread_kill

pthread_kill(tid, 0);

No se envía ninguna señal, pero aún se realiza la verificación de errores, por lo que puede usar eso para verificar la existencia de tid.

PRECAUCIÓN: Esta respuesta es incorrecta. El estándar prohíbe específicamente pasar la ID de un subproceso cuya vida útil haya terminado. Esa ID ahora podría especificar un subproceso diferente o, peor aún, podría referirse a la memoria que se ha liberado, lo que provoca un bloqueo.

  • Para mi problema, esto era exactamente lo que necesitaba para resolverlo, gracias. Usé (pthread_kill(tid, 0) != ESRCH).

    – leetNocturna

    26/10/2012 a las 15:21

  • Tenga en cuenta esto: stackoverflow.com/questions/1693180/…

    – Qiu Yang Fan

    13 de junio de 2015 a las 14:12

  • @DavidSchwartz En tales casos, es mejor marcar su edición claramente como algo que no es parte de la respuesta original, como se sugiere aquí.

    – Demonio

    20 de marzo de 2017 a las 21:43

  • Comentario adicional sobre la edición (correcta) de David Schwartz: la página de manual (Linux) dice esto: ‘POSIX.1-2008 recomienda que si una implementación detecta el uso de una ID de subproceso después del final de su vida útil, pthread_kill() debería devolver el error ESRCH. La implementación de glibc devuelve este error en los casos en que se puede detectar una ID de subproceso no válida. Pero tenga en cuenta también que POSIX dice que un intento de usar una ID de subproceso cuya vigencia ha terminado produce un comportamiento indefinido, y un intento de usar una ID de subproceso no válida en una llamada a pthread_kill() puede, por ejemplo, causar una falla de segmentación.

    – Erik Alapää

    27 de marzo de 2019 a las 8:47

Creo que todo lo que realmente necesitas es llamar a pthread_join(). Esa llamada no volverá hasta que el hilo haya salido.

Si solo desea sondear para ver si el subproceso aún se está ejecutando o no (¡y tenga en cuenta que eso no suele ser lo que debería querer hacer!), podría hacer que el subproceso establezca un booleano volátil en falso justo antes de que salga. Entonces, su subproceso principal podría leer el valor booleano y, si aún es cierto, sabrá que el subproceso aún se está ejecutando. (Si es falso, por otro lado, sabe que el subproceso al menos casi se ha ido; sin embargo, aún puede estar ejecutando el código de limpieza que ocurre después de establecer el booleano en falso, por lo que incluso en este caso aún debe llamar a pthread_join antes tratando de liberar los recursos a los que el subproceso podría tener acceso)

  • +1 por explicar la posible condición de carrera involucrada y una solución simple.

    – R.. GitHub DEJA DE AYUDAR A ICE

    13 de enero de 2011 a las 1:10

  • “entonces su subproceso principal podría leer el valor booleano y, si aún es cierto, sabrá que el subproceso aún se está ejecutando”. — En este caso, solo sabría si el subproceso ha salido limpiamente o no, lo que no es lo mismo que seguir ejecutándose o no.

    – usuario470379

    13 de enero de 2011 a las 21:14

  • Hmm, probablemente me estoy perdiendo una posibilidad aquí, pero AFAICT el hilo aún se está ejecutando, se cerró o se bloqueó. Y si el subproceso se ha bloqueado, se ha llevado consigo el resto del proceso, por lo que de todos modos no hay un segundo subproceso para detectarlo.

    –Jeremy Friesner

    3 de febrero de 2013 a las 4:30

No existe una solución totalmente portátil, compruebe si su plataforma es compatible con pthread_tryjoin_np o pthread_timedjoin_np. Entonces, solo verifica si se puede unir el hilo (por supuesto, creado con PTHREAD_CREATE_JOINABLE).

Permítanme señalar la respuesta “ganadora”, que tiene un gran defecto oculto y, en algunos contextos, puede provocar fallas. A menos que use pthread_join, aparecerá una y otra vez. Suponga que tiene un proceso y una biblioteca compartida. Llame a la biblioteca lib.so.

  1. Lo sumerges, comienzas un hilo en él. Suponga que no desea que se una a él, por lo que lo configura como desmontable.
  2. Proceso y lógica de lib compartida haciendo su trabajo, etc.
  3. Desea cargar lib.so, porque ya no lo necesita.
  4. Llamas a un apagado en el hilo y dices que quieres leer una bandera después del hilo de tu lib.so, que ha terminado.
  5. Continúas en otro hilo con dlclose, porque ves, que has visto, que la bandera ahora muestra el hilo como “terminado”
  6. dlclose cargará toda la memoria relacionada con la pila y el código.
  7. Vaya, pero dlclose no detiene los hilos. Y ya sabe, incluso cuando está en la última línea del controlador de limpieza para configurar la variable de bandera atómica volátil “thread is finish”, aún tiene que regresar de muchos métodos en la pila, devolver valores, etc. se le dio una gran prioridad al hilo #5+#6, recibirá dlclose antes de que REALMENTE pueda detenerse en el hilo. Tendrás algunas caídas agradables a veces.

Permítanme señalar que este no es un problema hipotético, tuve el mismo problema en nuestro proyecto.

avatar de usuario
eric buell

Creo que se me ocurrió una solución que al menos funciona para Linux. Cada vez que creo un hilo, guardo su LWP (ID de proceso de peso ligero) y le asigno un nombre único, por ejemplo. int lwp = llamada al sistema (SYS_gettid); prctl(PR_SET_NAME, (largo)”nombre único”, 0, 0, 0);

Luego, para verificar si el hilo existe más tarde, abro /proc/pid/tarea/lwp/comm y léalo. Si el archivo existe y su contenido coincide con el nombre único que asigné, el hilo existe. Tenga en cuenta que esto NO pasa un TID posiblemente inactivo/reutilizado a ninguna función de biblioteca, por lo que no se bloquea.

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/file.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <syscall.h>

pthread_t subthread_tid;
int       subthread_lwp;

#define UNIQUE_NAME "unique name"

bool thread_exists (pthread_t thread_id)
{
    char path[100];
    char thread_name[16];
    FILE *fp;
    bool  thread_exists = false;

    // If the /proc/<pid>/task/<lwp>/comm file exists and it's contents match the "unique name" the
    // thread exists, and it's the original thread (TID has NOT been reused).

    sprintf(path, "/proc/%d/task/%d/comm", getpid(), subthread_lwp);

    fp = fopen(path, "r");

    if( fp != NULL ) {

        fgets(thread_name, 16, fp);
        fclose(fp);

        // Need to trim off the newline
        thread_name[strlen(thread_name)-1] = '\0';

        if( strcmp(UNIQUE_NAME, thread_name) == 0 ) {
            thread_exists = true;
        }
    }

    if( thread_exists ) {
        printf("thread exists\n");
    } else {
        printf("thread does NOT exist\n");
    }

    return thread_exists;
}


void *subthread (void *unused)
{
    subthread_lwp = syscall(SYS_gettid);
    prctl(PR_SET_NAME, (long)UNIQUE_NAME, 0, 0, 0);

    sleep(10000);

    return NULL;
}


int main (int argc, char *argv[], char *envp[])
{
    int error_number;

    pthread_create(&subthread_tid, NULL, subthread, NULL);
    printf("pthread_create()\n");
    sleep(1);
    thread_exists(subthread_tid);

    pthread_cancel(subthread_tid);
    printf("pthread_cancel()\n");
    sleep(1);
    thread_exists(subthread_tid);

    error_number = pthread_join(subthread_tid, NULL);
    if( error_number == 0 ) {
        printf("pthread_join() successful\n");
    } else {
        printf("pthread_join() failed, %d\n", error_number);
    }
    thread_exists(subthread_tid);

    exit(0);
}

avatar de usuario
Priyesh Shah Pilu

#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>

void* thread1 (void* arg);
void* thread2 (void* arg);

int main()
{
    pthread_t thr_id;

    pthread_create(&thr_id, NULL, thread1, NULL);

    sleep(10);
}

void* thread1 (void* arg)
{
    pthread_t thr_id = 0;

    pthread_create(&thr_id, NULL, thread2, NULL);

    sleep(5);
    int ret = 0;
    if( (ret = pthread_kill(thr_id, 0)) == 0)
    {
        printf("still running\n");
        pthread_join(thr_id, NULL);
    }
    else
    {
        printf("RIP Thread = %d\n",ret);
    }
}

void* thread2 (void* arg)
{
//  sleep(5);
    printf("I am done\n");
}

  • Si bien este código puede responder a la pregunta, proporcionar un contexto adicional sobre por qué y/o cómo este código responde a la pregunta mejora su valor a largo plazo.

    – JAL

    23/10/2015 a las 23:51

  • Es posible que no lo sepa, pero hacer un pthread_kill en un hilo inactivo podría bloquear su aplicación o causar resultados inesperados, como desasignar la memoria utilizada en otra cosa. stackoverflow.com/a/1693235/105539

    – Volomike

    1 de abril de 2016 a las 7:32

¿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