web-dev-qa-db-ja.com

すべてのスレッドが完了するのをどのように待つことができますか?

終了する前に、すべての(p)スレッドが完了するのをメインスレッドに待機させたいだけです。

スレッドはさまざまな理由で頻繁に出入りしますが、すべてのスレッドを追跡したくはありません。それらがすべてなくなった時点で知りたいだけです。

wait()は子プロセスに対してこれを行い、子が残っていない場合にECHILDを返しますが、waitは(p)threadsを(動作するようには見えません)します。

すべての未処理のスレッドのリストを(それらが行き来するように)保持し、各スレッドでpthread_joinを呼び出さなければならないという面倒なことは本当にしたくない。

これをすばやく簡単に行う方法はありますか?

34
Brad

適切な方法は、すべてのpthread_idを追跡することですが、ここでそれは迅速で汚れた方法を要求しました。基本的に:

  • 実行中のスレッドの合計数を保持するだけで、
  • pthread_createを呼び出す前に、メインループでインクリメントします。
  • 各スレッドが終了するたびにスレッド数を減らします。
  • 次に、カウントが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);
  }
}
23
gravitron

すべてのスレッドが完了した後、メインスレッドに特に何かを実行させますか?

そうでない場合は、メインスレッドに、戻り値(またはpthread_exit())を呼び出す代わりに、exit()を呼び出すだけで済みます。

main()が返された場合、暗黙的にexit()を呼び出し(または呼び出したように動作します)、プロセスを終了します。ただし、main()が返される代わりにpthread_exit()を呼び出すと、exit()への暗黙的な呼び出しは発生せず、プロセスはすぐに終了しません-終了しますすべてのスレッドが終了したとき。

あまりにも速くn dirtierを取得することはできません。

以下に、違いを確認できる小さなサンプルプログラムを示します。パス -DUSE_PTHREAD_EXITすべてのスレッドが終了するまでプロセスが待機するのを確認するために、コンパイラに。そのマクロを定義せずにコンパイルして、プロセスがトラック内のスレッドを停止するのを確認します。

#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

スレッドを追跡したくない場合は、スレッドを切り離すことができるので、スレッドを気にする必要はありませんが、スレッドが終了したことを伝えるために、さらに先へ進む必要があります。

1つの秘Oneは、スレッドのステータスのリスト(リンクリスト、配列など)を保持することです。スレッドは、開始時に配列内のステータスをTHREAD_STATUS_RUNNINGなどに設定し、終了する直前にスレッドのステータスをTHREAD_STATUS_STOPPEDなどに更新します。その後、すべてのスレッドが停止したかどうかを確認したい場合は、この配列を反復処理してすべてのステータスを確認できます。

ただし、このようなことを行う場合、一度に1つのスレッドのみがアクセス(読み取りおよび書き込み)できるように配列へのアクセスを制御する必要があることを忘れないでください。ミューテックスを使用する必要があります。

2
SlappyTheFish

すべてのスレッドIDのリストを保持してから、各スレッドIDでpthread_joinを実行できます。もちろん、スレッドIDリストへのアクセスを制御するためにミューテックスが必要になります。また、反復中に変更できる何らかの種類のリスト、おそらく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

すばらしい回答をありがとうございました!メモリバリアなどの使用については多くの話がありました。そのため、それらが適切に使用されていることを示す回答を投稿すると思いました。

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

__syncマクロは「非標準」のGCC内部マクロであることに注意してください。 LLVMもこれらをサポートしていますが、別のコンパイラを使用している場合は、何か別のことをする必要があるかもしれません。

注意すべきもう1つの重要な点は、コア全体を燃やすのか、CPUの「半分」を他の人が終了するのを待つだけのタイトなポーリングループで浪費するのか-簡単に動作させることができるのはなぜですか?次のmodは初期スレッドを使用してワーカーの1つを実行し、他のワーカーが完了するのを待ちます。

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

「0」ではなく「1」で始まるスレッドの作成を開始し、「thread 0」をインラインで直接実行し、完了後にすべてのスレッドが完了するまで待機することに注意してください。一貫性のために&thread [0]を渡します(ここでは意味がありませんが)。実際には、おそらく独自の変数/コンテキストを渡します。

0
Brad