¿Cómo puedo esperar a que se completen algunos/todos los pthreads?


Solo quiero que mi hilo principal espere a que todos y cada uno de mis hilos (p)se completen antes de salir.

Los hilos van y vienen mucho 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 hijos, devolviendo ECHILD cuando no quedan hijos, sin embargo wait no (parece funcionar con) (p)hilos.

Realmente no quiero pasar por la molestia de mantener una lista de cada hilo sobresaliente (a medida que van y vienen), luego tener que llamar a pthread_join en cada uno.

Como hay una manera rápida y sucia de hacer esto?

Author: Brad, 2011-05-27

5 answers

La forma correcta es hacer un seguimiento de todos tus pthread_id, pero pediste una forma rápida y sucia, así que aquí está. Básicamente:

  • simplemente mantenga una cuenta total de hilos en ejecución,
  • lo incrementa en el bucle principal antes de llamar a pthread_create,
  • decrementa el número de hilos a medida que termina cada hilo.
  • Luego duerme al final del proceso principal hasta que el conteo vuelva 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);
  }
}
 21
Author: gravitron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-27 17:04:36

¿Quieres que tu hilo principal haga algo en particular después de que todos los hilos se hayan completado?

Si no, puede hacer que su hilo principal simplemente llame a pthread_exit() en lugar de regresar (o llamar a exit()).

If main() returns it implicitly calls (or behaves as if it called) exit(), which will terminate the process. Sin embargo, si main() llama a pthread_exit() en lugar de regresar, esa llamada implícita a exit() no se produce y el proceso no terminará inmediatamente, terminará cuando todos los subprocesos han terminado.

No puede ser demasiado rápido-n-sucio.

Aquí hay un pequeño programa de ejemplo que le permitirá ver la diferencia. Pase -DUSE_PTHREAD_EXIT al compilador para ver que el proceso espera a que todos los subprocesos finalicen. Compile sin esa macro definida para ver los subprocesos de parada del proceso 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;
}
 26
Author: Michael Burr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-27 20:26:20

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 están terminados, tendrá que ir un poco más lejos.

Un truco sería mantener una lista (lista vinculada, matriz, lo que sea) de los estados de los hilos. Cuando un subproceso comienza establece su estado en el array a algo como THREAD_STATUS_RUNNING y justo antes de que termine actualiza su estado a algo como THREAD_STATUS_STOPPED. Entonces cuando desee verificar si todos los subprocesos se han detenido, puede iterar sobre esta matriz y verificar todos los estados.

No olvides que si haces algo como esto, necesitarás controlar el acceso a la matriz para que solo un hilo pueda acceder (read y write) a la vez, por lo que necesitarás usar un mutex en él.

 2
Author: SlappyTheFish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-27 15:50:02

Usted podría mantener una lista de todos sus id de hilo y luego hacer pthread_join en cada uno, por supuesto, necesitará un mutex para controlar el acceso a la lista de id de subprocesos. usted también necesita algún tipo de lista que se puede modificar mientras se itera, tal vez un std::set?

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);
}
 1
Author: Nick Sotiros,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-15 11:07:47

Gracias a todos por las grandes respuestas! Se ha hablado mucho sobre el uso de barreras de memoria, etc., así que pensé en publicar una respuesta que mostrara correctamente cómo se usan para esto.

#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 GCC "no estándar". LLVM soporta estos también - pero si está usando otro compilador, puede que tenga que hacer algo diferente.

Otra gran cosa a tener en cuenta es: ¿Por qué quemar un núcleo entero, o desperdiciar "la mitad" de una CPU girando en un bucle de sondeo apretado solo esperando a que otros terminen, ¿cuando podrías ponerlo a trabajar fácilmente? El siguiente mod usa el hilo inicial para ejecutar uno de los workers, luego espera a que los otros se 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 que comienzan en "1" en lugar de "0", luego ejecutamos directamente "hilo 0" en línea, esperando que todos los hilos se completen después de que haya terminado. Le pasamos & thread[0] por coherencia (aunque no tenga sentido aquí), aunque en realidad probablemente le pasarías el tuyo propio variables/contexto.

 0
Author: Brad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-18 13:38:53