pthread_join - ¿Cómo puedo esperar para que se complete alguno/todos los pthreads?
pthread_create (5)
¡Gracias a todos por las excelentes respuestas! Se ha hablado mucho sobre el uso de barreras de memoria, etc., así que pensé en publicar una respuesta que los mostrara correctamente.
#define NUM_THREADS 5
unsigned int thread_count;
void *threadfunc(void *arg) {
printf("Thread %p running/n",arg);
sleep(3);
printf("Thread %p exiting/n",arg);
__sync_fetch_and_sub(&thread_count,1);
return 0L;
}
int main() {
int i;
pthread_t thread[NUM_THREADS];
thread_count=NUM_THREADS;
for (i=0;i<NUM_THREADS;i++) {
pthread_create(&thread[i],0L,threadfunc,&thread[i]);
}
do {
__sync_synchronize();
} while (thread_count);
printf("All threads done/n");
}
Tenga en cuenta que las macros __sync son macros internas de GCC "no estándar". LLVM es compatible con estos también, pero si utiliza otro compilador, es posible que tenga que hacer algo diferente.
Otra cosa importante a tener en cuenta es: ¿por qué quemaría un núcleo completo, o desperdiciará "la mitad" de una CPU girando en un círculo apretado esperando que otros terminen, cuando podría ponerlo a funcionar fácilmente? La siguiente mod usa el hilo inicial para ejecutar uno de los trabajadores, luego espera a que los demás completen:
thread_count=NUM_THREADS;
for (i=1;i<NUM_THREADS;i++) {
pthread_create(&thread[i],0L,threadfunc,&thread[i]);
}
threadfunc(&thread[0]);
do {
__sync_synchronize();
} while (thread_count);
printf("All threads done/n");
}
Tenga en cuenta que comenzamos a crear los hilos comenzando en "1" en lugar de "0", luego ejecutamos directamente "thread 0" en línea, esperando que todos los hilos se completen una vez que estén listos. Pasamos y le aplicamos [0] coherencia (aunque no tenga sentido aquí), aunque en realidad es probable que pase sus propias variables / contexto.
Solo quiero que mi hilo principal espere que todos mis hilos (p) se completen antes de salir.
Los hilos van y vienen por diferentes razones, y realmente no quiero hacer un seguimiento de todos ellos, solo quiero saber cuándo se han ido todos.
wait () hace esto para procesos secundarios, devolviendo ECHILD cuando no quedan hijos, sin embargo, wait no funciona (parece que funciona con) (p).
Realmente no quiero tomar el trabajo de mantener una lista de cada hilo destacado (a medida que van y vienen), y luego tener que llamar a pthread_join en cada uno.
Como hay una manera rápida y sucia de hacer esto?
¿Desea que su hilo principal haga algo en particular después de que se hayan completado todos los hilos?
Si no, puede hacer que su hilo principal simplemente llame a pthread_exit()
lugar de regresar (o llamar a exit()
).
Si main()
devuelve llama implícitamente (o se comporta como si lo llamara) exit()
, lo que dará por terminado el proceso. Sin embargo, si main()
llama a pthread_exit()
lugar de devolver, esa llamada implícita a exit()
no se produce y el proceso no finalizará de inmediato: terminará cuando todos los hilos hayan terminado.
No se puede obtener demasiado rápido y sucio.
Aquí hay un pequeño ejemplo de programa que te permitirá ver la diferencia. Pase -DUSE_PTHREAD_EXIT
al compilador para ver el proceso esperar a que finalicen todos los subprocesos. Compile sin esa macro definida para ver el proceso detener hilos en sus pistas.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
static
void sleep(int ms)
{
struct timespec waittime;
waittime.tv_sec = (ms / 1000);
ms = ms % 1000;
waittime.tv_nsec = ms * 1000 * 1000;
nanosleep( &waittime, NULL);
}
void* threadfunc( void* c)
{
int id = (int) c;
int i = 0;
for (i = 0 ; i < 12; ++i) {
printf( "thread %d, iteration %d/n", id, i);
sleep(10);
}
return 0;
}
int main()
{
int i = 4;
for (; i; --i) {
pthread_t* tcb = malloc( sizeof(*tcb));
pthread_create( tcb, NULL, threadfunc, (void*) i);
}
sleep(40);
#ifdef USE_PTHREAD_EXIT
pthread_exit(0);
#endif
return 0;
}
La forma correcta es realizar un seguimiento de todos sus pthread_id, pero usted pidió una manera rápida y sucia, así que aquí está. Básicamente:
- solo mantenga un recuento total de los subprocesos,
- increméntelo en el ciclo principal antes de llamar a pthread_create,
- disminuya el número de subprocesos a medida que termina cada subproceso.
- Luego, duerma al final del proceso principal hasta que el recuento regrese a 0.
.
volatile int running_threads = 0;
pthread_mutex_t running_mutex = PTHREAD_MUTEX_INITIALIZER;
void * threadStart()
{
// do the thread work
pthread_mutex_lock(&running_mutex);
running_threads--;
pthread_mutex_unlock(&running_mutex);
}
int main()
{
for (i = 0; i < num_threads;i++)
{
pthread_mutex_lock(&running_mutex);
running_threads++;
pthread_mutex_unlock(&running_mutex);
// launch thread
}
while (running_threads > 0)
{
sleep(1);
}
}
Si no desea realizar un seguimiento de sus hilos, puede separar los hilos para que no tenga que preocuparse por ellos, pero para saber cuándo terminaron, tendrá que ir un poco más allá.
Un truco sería mantener una lista (lista vinculada, matriz, lo que sea) de los estados de los hilos. Cuando se inicia una secuencia, establece su estado en la matriz en algo como THREAD_STATUS_RUNNING y justo antes de que termine, actualiza su estado a algo así como THREAD_STATUS_STOPPED. Luego, cuando desee verificar si todos los hilos se han detenido, puede simplemente iterar sobre esta matriz y verificar todos los estados.
Sin embargo, no olvide que si hace algo como esto, tendrá que controlar el acceso a la matriz para que solo un hilo pueda acceder (leer y escribir) a la vez, por lo que tendrá que usar un mutex en él. .
puedes mantener una lista de todos tus identificadores de subprocesos y luego pthread_join en cada uno, por supuesto necesitarás un mutex para controlar el acceso a la lista de identificación de subprocesos. también necesitarás algún tipo de lista que se pueda modificar mientras se itera, tal vez un std :: set <pthread_t>?
int main() {
pthread_mutex_lock(&mutex);
void *data;
for(threadId in threadIdList) {
pthread_mutex_unlock(&mutex);
pthread_join(threadId, &data);
pthread_mutex_lock(&mutex);
}
printf("All threads completed./n");
}
// called by any thread to create another
void CreateThread()
{
pthread_t id;
pthread_mutex_lock(&mutex);
pthread_create(&id, NULL, ThreadInit, &id); // pass the id so the thread can use it with to remove itself
threadIdList.add(id);
pthread_mutex_unlock(&mutex);
}
// called by each thread before it dies
void RemoveThread(pthread_t& id)
{
pthread_mutex_lock(&mutex);
threadIdList.remove(id);
pthread_mutex_unlock(&mutex);
}