It seems as though you misunderstand the usefulness of CTPL.
From CTPL README.md:
... The jobs come to the pool dynamically. A job is fetched and deleted
from the container when there is an idle thread. The job is then run
on that thread.
A thread pool is helpful when you want to minimize time of loading and
destroying threads and when you want to limit the number of parallel
jobs that run simultaneously ...
If you want to run the thread pool once and then return the function once all the threads are done simply do the following:
void runner(int id, int a, int b) {...}
void caller()
{
int nbThreads = 4;
ctpl::thread_pool pool(4);
for (int i = 0; i < nbThreads; i++) {
pool.push(runner, a, b);
}
}
The caller function will not return until the thread_pool's threads have finished. This effectively synchronizes the threads.
The benefit of CTPL's thread_pool is you can push a job to the thread pool without worrying about the status of the other threads. e.g.
void runner(int id, int a, int b) {...}
void runner2(int id) {...}
// I would wrap this in it's own class...
static ctpl::thread_pool pool(4);
void caller()
{
int nbThreads = 25;
for (int i = 0; i < nbThreads; i++) {
pool.push(runner, a, b);
}
}
// caller and caller2 called at different times in main thread.
void caller2(int jobs)
{
for (int i = 0; i < jobs; i++) {
pool.push(runner2);
}
}
This example should synchronize all threads before the process ends.
Based on the answer you provided yourself, the functionality you desire seems to differ from what the library provides. The biggest issue is that if you're waiting in a for-loop you're wasting time. In your comment, you say that creating new threads every time you call caller
would waste time. In your answer, what if results[0] takes ten minutes to run but rest of the results finished in seconds?
If you truly want to run N jobs and wait for them to be done while using the CTPL library. I recommend altering your answer a bit to at catch any exceptions that might have been thrown during the jobs.
void runner(int id, int a, int b) {...}
void caller()
{
int jobs = 35;
static ctpl::thread_pool pool(4);
std::vector<std::future<void>> job_results;
for (int i = 0; i < jobs; i++) {
job_results.push_back(pool.push(runner, a, b));
}
for (std::future<void> result : job_results) {
try {
results.get();
} catch (const std::exception& e) {
... // handle exception.
}
}
}
However, you would be best off just using a static vector of std::threads for the functionality you want. That way you don't have any 3rd party dependencies.