Linux 如何等待任何/所有 pthread 完成?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6154539/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 00:58:25  来源:igfitidea点击:

How can I wait for any/all pthreads to complete?

clinuxmultithreadingpthreadsposix-api

提问by Brad

I just want my main thread to wait for any and all my (p)threads to complete before exiting.

我只希望我的主线程在退出之前等待我的所有 (p) 线程完成。

The threads come and go a lot for different reasons, and I really don't want to keep track of all of them - I just want to know when they're all gone.

由于不同的原因,这些线程来来往往,我真的不想跟踪所有这些 - 我只想知道它们什么时候都消失了。

wait() does this for child processes, returning ECHILD when there are no children left, however wait does not (appear to work with) (p)threads.

wait() 为子进程执行此操作,当没有子进程时返回 ECHILD,但是 wait 不会(似乎与)(p)线程一起使用。

I really don't want to go through the trouble of keeping a list of every single outstanding thread (as they come and go), then having to call pthread_join on each.

我真的不想经历保留每个未完成线程的列表(随着它们来来去去)的麻烦,然后不得不在每个线程上调用 pthread_join。

As there a quick-and-dirty way to do this?

因为有一种快速而肮脏的方法来做到这一点?

采纳答案by gravitron

The proper way is to keep track of all of your pthread_id's, but you asked for a quick and dirty way so here it is. Basically:

正确的方法是跟踪您所有的 pthread_id,但是您要求使用一种快速而肮脏的方法,所以它就在这里。基本上:

  • just keep a total count of running threads,
  • increment it in the main loop before calling pthread_create,
  • decrement the thread count as each thread finishes.
  • Then sleep at the end of the main process until the count returns to 0.
  • 只需保持运行线程的总数,
  • 在调用 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);
  }
}

回答by SlappyTheFish

If you don't want to keep track of your threads then you can detach the threads so you don't have to care about them, but in order to tell when they are finished you will have to go a bit further.

如果您不想跟踪您的线程,那么您可以分离线程,这样您就不必关心它们,但是为了知道它们何时完成,您将不得不走得更远。

One trick would be to keep a list (linked list, array, whatever) of the threads' statuses. When a thread starts it sets its status in the array to something like THREAD_STATUS_RUNNING and just before it ends it updates its status to something like THREAD_STATUS_STOPPED. Then when you want to check if all threads have stopped you can just iterate over this array and check all the statuses.

一个技巧是保留线程状态的列表(链表、数组等)。当线程启动时,它会将其在数组中的状态设置为 THREAD_STATUS_RUNNING 之类的内容,并在结束之前将其状态更新为 THREAD_STATUS_STOPPED 之类的内容。然后,当您想检查所有线程是否已停止时,您只需遍历此数组并检查所有状态。

Don't forget though that if you do something like this, you will need to control access to the array so that only one thread can access (read andwrite) it at a time, so you'll need to use a mutex on it.

不要忘记,如果你做这样的事情,你将需要控制对数组的访问,以便一次只有一个线程可以访问(读取写入)它,所以你需要在它上面使用互斥锁.

回答by Michael Burr

Do you want your main thread to do anything in particular after all the threads have completed?

在所有线程完成后,您是否希望主线程执行任何特定操作?

If not, you can have your main thread simply call pthread_exit()instead of returning (or calling exit()).

如果没有,您可以让您的主线程简单地调用pthread_exit()而不是返回(或调用exit())。

If main()returns it implicitly calls (or behaves as if it called) exit(), which will terminate the process. However, if main()calls pthread_exit()instead of returning, that implicit call to exit()doesn't occur and the process won't immediately end - it'll end when all threads have terminated.

如果main()返回它隐式调用(或表现得好像它调用了)exit(),这将终止进程。但是,如果main()调用pthread_exit()而不是返回,exit()则不会发生隐式调用,并且进程不会立即结束 - 它会在所有线程终止时结束。

Can't get too much quick-n-dirtier.

不能得到太多的快速和肮脏。

Here's a small example program that will let you see the difference. Pass -DUSE_PTHREAD_EXITto the compiler to see the process wait for all threads to finish. Compile without that macro defined to see the process stop threads in their tracks.

这是一个小示例程序,可以让您看到差异。传递-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;
}

回答by Nick Sotiros

you could keep a list all your thread ids and then do pthread_join on each one, of course you will need a mutex to control access to the thread id list. you will also need some kind of list that can be modified while being iterated on, maybe a std::set<pthread_t>?

您可以保留所有线程 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);
}

回答by Brad

Thanks all for the great answers! There has been a lot of talk about using memory barriers etc - so I figured I'd post an answer that properly showed them used for this.

感谢大家的精彩回答!有很多关于使用内存屏障等的讨论 - 所以我想我会发布一个正确显示它们用于此的答案。

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

Note that the __sync macros are "non-standard" GCC internal macros. LLVM supports these too - but if your using another compiler, you may have to do something different.

请注意,__sync 宏是“非标准”GCC 内部宏。LLVM 也支持这些——但如果你使用另一个编译器,你可能需要做一些不同的事情。

Another big thing to note is: Why would you burn an entire core, or waste "half" of a CPU spinning in a tight poll-loop just waiting for others to finish - when you could easily put it to work? The following mod uses the initial thread to run one of the workers, then wait for the others to complete:

另一件需要注意的大事是:为什么要烧掉整个内核,或者在一个紧密的轮询循环中浪费“一半”的 CPU 旋转,只是等待其他人完成 - 当您可以轻松地使其工作时?以下 mod 使用初始线程运行其中一个工作线程,然后等待其他线程完成:

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

Note that we start creating the threads starting at "1" instead of "0", then directly run "thread 0" inline, waiting for all threads to complete after it's done. We pass &thread[0] to it for consistency (even though it's meaningless here), though in reality you'd probably pass your own variables/context.

请注意,我们从“1”而不是“0”开始创建线程,然后直接内联运行“线程 0”,在完成后等待所有线程完成。我们将 &thread[0] 传递给它以保持一致性(即使它在这里毫无意义),但实际上您可能会传递自己的变量/上下文。