Одним простым способом является использование канала в качестве канала связи между (рабочими) потоками и вашим основным потоком. Когда поток завершается, он записывает свой результат (идентификатор потока в следующем примере) в канал. Основной поток ожидает в канале и считывает результат потока из него, как только он становится доступным.
В отличие от мьютекса или семафора, дескриптор файла канала может быть легко обработан главным циклом событий приложения (например, libevent). Записи из разных потоков в один и тот же канал являются атомарными, если они записывают PIPE_BUF
или менее байтов (4096 в моем Linux).
Ниже приведена демонстрация, которая создает десять потоков, каждый из которых имеет различную продолжительность жизни. Затем основной поток ожидает завершения какого-либо потока и печатает его идентификатор. Завершается, когда завершены все десять потоков.
$ 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