Reputation: 21
I am looking for the optimal way in terms of execution time, to wait for independent futures to finish.
Dealing with only two futures is simple, one can have the optimal way as follows:
auto f1 = async(launch::async, []{ doSomething(’.’); });
auto f2 = async(launch::async, []{ doSomething(’+’); });
while (f1.wait_for(chrono::seconds(0)) != future_status::ready && f2.wait_for(chrono::seconds(0)) != future_status::ready)
{ };
f1.get();
f2.get();
This way, we leave the loop while with at least one of the futures is finished, then calling .get()
for both won't make the program looses time.
How about n futures?
Upvotes: 2
Views: 4123
Reputation: 884
If you want to create an arbitrary number of std::futures
, it might help to put them all into a std::vector
. You can then loop through the vector and get
the results. Note that get
handles waiting.
//Result type defined here: http://en.cppreference.com/w/cpp/thread/async
template<typename F, typename... Args>
using AsyncResult = std::result_of_t<std::decay_t<F>(std::decay_t<Args>...)>;
template<typename T, typename F1, typename F2>
void do_par(const std::vector<T>& ts, F1&& on_t, F2&& accumulate) {
//The standard doesn't require that these futures correspond to individual
//threads, but there's a good chance they'll be implemented that way.
std::vector<std::future<AsyncResult<F1, T>>> threads;
for (const T& t : ts) { threads.push_back(std::async(on_t, t)); }
for (auto& future : threads) { accumulate(std::move(future)); }
}
template<typename T, typename F>
std::vector<AsyncResult<F, T>> map_par(const std::vector<T>& ts, F&& on_t) {
std::vector<AsyncResult<F, T>> out;
do_par(ts, on_t, [&](auto&& future_){
out.push_back(future_.get()); //Think of this as just waiting on each thread to finish.
});
return out;
}
std::string doSomething(const std::string&){ return std::string("yo"); }
And then you can do
const std::vector<std::string> results = map_par(
std::vector<std::string>{".", "+", "et al"}, doSomething
);
This simple map_par
function isn't quite the savviest solution. It might help to set up a thread queue (which itself would own a thread pool) to cut out the overhead of spawning individual threads, as well as the context-switching overhead which comes into play when you have more threads than CPU cores. Your thread queue implementation might want to have its own async
method, akin to std::async
.
If you want to use results immediately when they come in, regardless of input order, consider setting up a single-reader-multiple-writer (which incidentally also involves a queue).
std::condition_variable
helps with both of the above suggestions.
Upvotes: 1