我只希望我的主线程在退出之前等待我的所有(p)线程完成。
由于不同的原因,线程来来去去很多,我真的不想跟踪所有这些 - 我只想知道它们什么时候都消失了。
wait() 对子进程执行此操作,当没有子进程时返回 ECHILD,但是 wait 不会(似乎可以使用)(p)线程。
我真的不想麻烦地保留每个未完成线程的列表(因为它们来来去去),然后不得不在每个线程上调用 pthread_join。
因为有一种快速而肮脏的方法来做到这一点?
我只希望我的主线程在退出之前等待我的所有(p)线程完成。
由于不同的原因,线程来来去去很多,我真的不想跟踪所有这些 - 我只想知道它们什么时候都消失了。
wait() 对子进程执行此操作,当没有子进程时返回 ECHILD,但是 wait 不会(似乎可以使用)(p)线程。
我真的不想麻烦地保留每个未完成线程的列表(因为它们来来去去),然后不得不在每个线程上调用 pthread_join。
因为有一种快速而肮脏的方法来做到这一点?
在所有线程完成后,您是否希望您的主线程特别做任何事情?
如果没有,您可以让您的主线程简单地调用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;
}
正确的方法是跟踪你所有的 pthread_id,但是你要求一种快速而肮脏的方法,所以就在这里。基本上:
.
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);
}
}
如果你不想跟踪你的线程,那么你可以分离线程,这样你就不必关心它们,但是为了知道它们何时完成,你必须走得更远一些。
一个技巧是保留线程状态的列表(链表、数组等)。当一个线程启动时,它会将其在数组中的状态设置为类似于 THREAD_STATUS_RUNNING 的值,并且在它结束之前,它会将其状态更新为类似于 THREAD_STATUS_STOPPED 的值。然后,当您想检查是否所有线程都已停止时,您可以遍历该数组并检查所有状态。
不要忘记,如果您执行此类操作,您将需要控制对数组的访问,以便一次只有一个线程可以访问(读取和写入)它,因此您需要在其上使用互斥锁.
您可以保留所有线程 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);
}
感谢大家的精彩回答!有很多关于使用内存屏障等的讨论 - 所以我想我会发布一个正确显示它们用于此目的的答案。
#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 也支持这些 - 但如果您使用另一个编译器,您可能需要做一些不同的事情。
另一件需要注意的大事是:你为什么要烧掉整个内核,或者浪费“一半”的 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");
}
请注意,我们从“1”而不是“0”开始创建线程,然后直接运行“thread 0”内联,等待所有线程完成后完成。我们将 &thread[0] 传递给它以保持一致性(即使它在这里毫无意义),尽管实际上您可能会传递您自己的变量/上下文。