it-swarm.com.de

Wie kann ich warten, bis alle Threads abgeschlossen sind?

Ich möchte nur, dass mein Haupt-Thread wartet, bis alle (p) Threads vor dem Beenden abgeschlossen sind. 

Die Threads kommen und gehen aus verschiedenen Gründen sehr viel, und ich möchte wirklich nicht alle verfolgen - ich möchte nur wissen, wann sie alle verschwunden sind.

wait () führt dies für untergeordnete Prozesse aus. ECHILD wird zurückgegeben, wenn keine Kinder übrig sind, aber wait (scheint nicht zu funktionieren) mit (p) -Threads.

Ich möchte wirklich nicht die Mühe machen, eine Liste aller einzelnen ausstehenden Threads zu führen (wie sie kommen und gehen), und dann jeweils pthread_join aufrufen zu müssen.

Wie gibt es eine schnelle und schmutzige Möglichkeit, dies zu tun?

34
Brad

Der richtige Weg ist, alle Ihre pthread_id's zu verfolgen, aber Sie haben nach einem schnellen und schmutzigen Weg gefragt, also hier ist er. Grundsätzlich gilt:

  • behalte nur die Gesamtzahl der laufenden Threads, 
  • inkrementieren Sie es in der Hauptschleife, bevor Sie pthread_create aufrufen, 
  • verringern Sie die Threadanzahl, wenn jeder Thread beendet ist. 
  • Schlafen Sie dann am Ende des Hauptprozesses, bis der Zähler auf 0 zurückkehrt.

.

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);
  }
}
22
gravitron

Möchten Sie, dass Ihr Haupt-Thread etwas Besonderes tut, nachdem alle Threads abgeschlossen sind?

Wenn nicht, können Sie Ihren Haupt-Thread einfach pthread_exit() aufrufen, anstatt zurückzukehren (oder exit() aufzurufen). 

Wenn main() zurückgegeben wird, ruft es implizit auf (oder verhält sich, als würde es aufgerufen) exit(), wodurch der Prozess beendet wird. Wenn main() jedoch pthread_exit() aufruft, statt zurückzukehren, findet der implizite Aufruf von exit() nicht statt und der Prozess wird nicht sofort beendet - er endet, wenn alle Threads beendet sind.

Kann nicht zu viel schneller und schmutziger werden.

Hier ist ein kleines Beispielprogramm, mit dem Sie den Unterschied erkennen können. Übergeben Sie -DUSE_PTHREAD_EXIT an den Compiler, um zu sehen, wie der Prozess wartet, bis alle Threads abgeschlossen sind. Kompilieren Sie, ohne dass das Makro definiert ist, um die Prozessstops in ihren Spuren zu sehen. 

#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
Michael Burr

Wenn Sie Ihre Threads nicht im Auge behalten möchten, können Sie die Threads abtrennen, sodass Sie sich nicht um sie kümmern müssen. Wenn Sie jedoch wissen möchten, wann sie fertig sind, müssen Sie noch etwas weiter gehen.

Ein Trick wäre, eine Liste (Statusliste, Array, was auch immer) der Threads zu führen. Wenn ein Thread gestartet wird, setzt er seinen Status im Array auf etwas wie THREAD_STATUS_RUNNING und aktualisiert kurz vor seinem Ende seinen Status auf etwas wie THREAD_STATUS_STOPPED. Wenn Sie dann überprüfen möchten, ob alle Threads angehalten wurden, können Sie einfach dieses Array durchlaufen und alle Status überprüfen.

Vergessen Sie jedoch nicht, dass Sie, wenn Sie so etwas tun, den Zugriff auf das Array so steuern müssen, dass jeweils nur ein Thread auf ihn zugreifen kann (read und write) ein Mutex darauf.

2
SlappyTheFish

sie könnten eine Liste aller Ihrer Thread-IDs führen und dann pthread_join für jede tun. Sie werden auch eine Art Liste benötigen, die während der Iteration modifiziert werden kann, möglicherweise ein 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);
}
1
Nick Sotiros

Vielen Dank für die tollen Antworten! Es wurde viel über den Einsatz von Gedächtnisbarrieren usw. gesprochen - also dachte ich, ich würde eine Antwort posten, die sie richtig zeigt. 

#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");
}

Beachten Sie, dass die __sync-Makros "nicht standardmäßige" GCC-interne Makros sind. LLVM unterstützt auch diese - aber wenn Sie einen anderen Compiler verwenden, müssen Sie möglicherweise etwas anderes tun.

Eine weitere große Sache, die Sie beachten sollten: Warum würden Sie einen ganzen Kern verbrennen oder "die Hälfte" einer CPU verschwenden, die sich in einer engen Abfrageschleife dreht, und nur darauf warten, dass andere fertig sind - wenn Sie es problemlos schaffen könnten? Der folgende Mod verwendet den anfänglichen Thread, um einen der Arbeiter auszuführen, und wartet dann, bis die anderen abgeschlossen sind:

  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");
}

Beachten Sie, dass wir die Threads beginnend mit "1" anstelle von "0" erstellen. Anschließend führen Sie "Thread 0" direkt inline aus und warten, bis alle Threads abgeschlossen sind. Wir übergeben & thread [0] für Konsistenz (obwohl dies hier bedeutungslos ist), obwohl Sie in der Realität wahrscheinlich Ihre eigenen Variablen/Kontext übergeben würden.

0
Brad