One easy way is to use a pipe as a communication channel between the (worker) threads and your main thread. When a thread terminates it writes its result (thread id in the following example) to the pipe. The main thread waits on the pipe and reads the thread result from it as soon as it becomes available.
Unlike mutex or semaphore, a pipe file descriptor can be easily handled by the application main event loop (such as libevent). The writes from different threads to the same pipe are atomic as long as they write PIPE_BUF
or less bytes (4096 on my Linux).
Below is a demo that creates ten threads each of which has a different life span. Then the main thread waits for any thread to terminate and prints its thread id. It terminates when all ten threads have completed.
$ cat test.cc
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
void* thread_fun(void* arg) {
// do something
unsigned delay = rand() % 10;
usleep(delay * 1000000);
// notify termination
int* thread_completed_fd = static_cast<int*>(arg);
pthread_t thread_id = pthread_self();
if(sizeof thread_id != write(*thread_completed_fd, &thread_id, sizeof thread_id))
abort();
return 0;
}
int main() {
int fd[2];
if(pipe(fd))
abort();
enum { THREADS = 10 };
time_t start = time(NULL);
// start threads
for(int n = THREADS; n--;) {
pthread_t thread_id;
if(pthread_create(&thread_id, NULL, thread_fun, fd + 1))
abort();
std::cout << time(NULL) - start << " sec: started thread " << thread_id << '\n';
}
// wait for the threads to finish
for(int n = THREADS; n--;) {
pthread_t thread_id;
if(sizeof thread_id != read(fd[0], &thread_id, sizeof thread_id))
abort();
if(pthread_join(thread_id, NULL)) // detached threads don't need this call
abort();
std::cout << time(NULL) - start << " sec: thread " << thread_id << " has completed\n";
}
close(fd[0]);
close(fd[1]);
}
$ g++ -o test -pthread -Wall -Wextra -march=native test.cc
$ ./test
0 sec: started thread 140672287479552
0 sec: started thread 140672278759168
0 sec: started thread 140672270038784
0 sec: started thread 140672261318400
0 sec: started thread 140672252598016
0 sec: started thread 140672243877632
0 sec: started thread 140672235157248
0 sec: started thread 140672226436864
0 sec: started thread 140672217716480
0 sec: started thread 140672208996096
1 sec: thread 140672208996096 has completed
2 sec: thread 140672226436864 has completed
3 sec: thread 140672287479552 has completed
3 sec: thread 140672243877632 has completed
5 sec: thread 140672252598016 has completed
5 sec: thread 140672261318400 has completed
6 sec: thread 140672278759168 has completed
6 sec: thread 140672235157248 has completed
7 sec: thread 140672270038784 has completed
9 sec: thread 140672217716480 has completed