pthread_create - ¿Cuándo usar pthread_exit() y cuándo usar pthread_join() en Linux?
pthreads php (7)
Soy nuevo en pthreads, y estoy tratando de entenderlo. Vi algunos ejemplos como el siguiente.
Pude ver que main()
está bloqueado por la API pthread_exit()
, y he visto ejemplos donde la API pthread_join()
bloquea la función principal. No puedo entender cuándo usar qué?
Me refiero al siguiente sitio: https://computing.llnl.gov/tutorials/pthreads/ . No puedo obtener el concepto de cuándo usar pthread_join()
y cuándo usar pthread_exit()
.
¿Alguien puede explicar? Además, se apreciará un buen enlace de tutorial para pthreads.
#include <pthread.h>
#include <stdio.h>
#define NUM_THREADS 5
void *PrintHello(void *threadid)
{
long tid;
tid = (long)threadid;
printf("Hello World! It''s me, thread #%ld!/n", tid);
pthread_exit(NULL);
}
int main (int argc, char *argv[])
{
pthread_t threads[NUM_THREADS];
int rc;
long t;
for(t=0; t<NUM_THREADS; t++){
printf("In main: creating thread %ld/n", t);
rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
if (rc){
printf("ERROR; return code from pthread_create() is %d/n", rc);
exit(-1);
}
}
/* Last thing that main() should do */
pthread_exit(NULL);
Realizado una cosa más es decir
pthread_cancel(thread);
pthread_join(thread, NULL);
A veces, desea cancelar el hilo mientras se está ejecutando. Puedes hacer esto usando pthread_cancel (thread) ;. Sin embargo, recuerde que debe habilitar el soporte de cancelación de pthread. Además, un código de limpieza luego de la cancelación.
thread_cleanup_push(my_thread_cleanup_handler, resources);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
static void my_thread_cleanup_handler(void *arg)
{
// free
// close, fclose
}
La API pthread_exit ()
como ya se ha comentado, se usa para la terminación de la secuencia de llamada. Después de una llamada a esa función, se inicia un mecanismo de limpieza complicado. Cuando finaliza, el hilo finaliza. La API pthread_exit () también se invoca implícitamente cuando se produce una llamada a la rutina return () en un subproceso creado por pthread_create (). En realidad, una llamada a return () y una llamada a pthread_exit () tienen el mismo impacto, al ser llamadas desde un subproceso creado por pthread_create ().
Es muy importante distinguir el hilo inicial, creado implícitamente cuando se inicia la función main (), y los hilos creados por pthread_create (). Una llamada a la rutina return () desde la función main () invoca implícitamente la llamada al sistema exit () y todo el proceso finaliza. No se inicia ningún mecanismo de limpieza de hilo. Una llamada a pthread_exit () desde la función main () hace que el mecanismo de limpieza se inicie y cuando termina su trabajo, el hilo inicial finaliza.
Lo que ocurre con todo el proceso (y con otros hilos) cuando se llama a pthread_exit () desde la función main () depende de la implementación PTHREAD. Por ejemplo, en la implementación de IBM OS / 400 todo el proceso finaliza, incluidos otros subprocesos, cuando se llama a pthread_exit () desde la función main (). Otros sistemas pueden comportarse de manera diferente. En la mayoría de las máquinas Linux modernas, una llamada a pthread_exit () desde el hilo inicial no finaliza todo el proceso hasta que finaliza todo el proceso. Tenga cuidado al usar pthread_exit () desde main (), si desea escribir una aplicación portátil.
La API pthread_join ()
es una forma conveniente de esperar la finalización de un hilo. Puede escribir su propia función que espera la terminación de un hilo, tal vez más adecuada para su aplicación, en lugar de usar pthread_join (). Por ejemplo, puede ser una función basada en esperar en variables condicionales.
Lo recomendaría para leer un libro de David R. Butenhof "Programación con hilos POSIX". Explica los temas discutidos (y cosas más complicadas) muy bien (aunque algunos detalles de implementación, como el uso de pthread_exit en la función principal, no siempre se reflejan en el libro).
Ambos métodos garantizan que su proceso no finalice antes de que todos sus hilos hayan finalizado.
El método de unión tiene su hilo de la función main
espera explícitamente para todos los hilos que se van a "unir".
El método pthread_exit
termina su función main
y el hilo de una manera controlada. main
tiene la particularidad de que terminar lo main
contrario terminaría todo el proceso, incluidos todos los demás hilos.
Para que esto funcione, debes asegurarte de que ninguno de tus subprocesos está utilizando variables locales que están declaradas dentro de la función main
. La ventaja de ese método es que su main
no tiene que conocer todos los hilos que se han iniciado en su proceso, por ejemplo, porque otros hilos han creado nuevos hilos de los que el main
no sabe nada.
Como se explica en la documentación de OpenPub,
pthread_exit()
saldrá del hilo que lo llama.
En su caso, dado que el principal lo llama, el hilo principal terminará mientras que los hilos engendrados continuarán ejecutándose. Esto se usa principalmente en casos donde el hilo principal solo es necesario para engendrar hilos y dejar los hilos para hacer su trabajo
pthread_join
suspenderá la ejecución del subproceso que lo ha llamado a menos que el subproceso final termine
Esto es útil en los casos en que desea esperar a que el / los hilo (s) finalicen antes del procesamiento posterior en el hilo principal.
Hmm.
Descripción de POSIX pthread_exit
de http://pubs.opengroup.org/onlinepubs/009604599/functions/pthread_exit.html :
After a thread has terminated, the result of access to local (auto) variables of the thread is
undefined. Thus, references to local variables of the exiting thread should not be used for
the pthread_exit() value_ptr parameter value.
Lo cual parece contrario a la idea de que las variables de subproceso main () locales permanecerán accesibles.
No necesita ninguna llamada a pthread_exit(3)
en su código particular.
En general, el hilo main
no debe llamar a pthread_exit
, pero a menudo debe llamar a pthread_join(3)
para esperar a que termine otro subproceso.
En su función PrintHello
, no necesita llamar a pthread_exit
porque está implícito después de regresar de él.
Entonces tu código debería ser:
void *PrintHello(void *threadid) {
long tid = (long)threadid;
printf("Hello World! It''s me, thread #%ld!/n", tid);
return threadid;
}
int main (int argc, char *argv[]) {
pthread_t threads[NUM_THREADS];
int rc;
intptr_t t;
// create all the threads
for(t=0; t<NUM_THREADS; t++){
printf("In main: creating thread %ld/n", (long) t);
rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
if (rc) { fprintf(stderr, "failed to create thread #%ld - %s/n",
(long)t, strerror(rc));
exit(EXIT_FAILURE);
};
}
pthread_yield(); // useful to give other threads more chance to run
// join all the threads
for(t=0; t<NUM_THREADS; t++){
printf("In main: joining thread #%ld/n", (long) t);
rc = pthread_join(&threads[t], NULL);
if (rc) { fprintf(stderr, "failed to join thread #%ld - %s/n",
(long)t, strerror(rc));
exit(EXIT_FAILURE);
}
}
}
pthread_exit()
terminará el hilo de llamada y saldrá de él (pero los recursos utilizados por el hilo de llamada no se lanzan al sistema operativo si no están separados del hilo principal).
pthrade_join()
esperará o bloqueará el hilo de llamada hasta que el hilo de destino no finalice. En palabras simples esperará a salir del hilo de destino.
En su código, si pone sleep (o delay) en la función PrintHello
antes de pthread_exit()
, entonces el hilo principal puede salir y finalizar el proceso completo. Aunque su función PrintHello
no se completa, terminará. Si utiliza la función pthrade_join()
en main antes de llamar a pthread_exit()
desde main, bloqueará el hilo principal y esperará a completar el hilo de llamada ( PrintHello
).
pthread_exit
finaliza el hilo de pthread_join
mientras pthread_join
suspende la ejecución del hilo de llamada hasta que los hilos de destino finalicen la ejecución.
Están bastante bien explicados en detalle en la documentación del grupo abierto: