`std::async` starts a task whose result is not needed immediately and returns a `std::future` for that result. Unlike `std::thread`, it provides a direct way to retrieve the return value: do other work, then call `get()`. If the result is not ready, `get()` blocks until it is. section 4.2.1
Listing 4.6. Using std::future to get the return value of an asynchronous task
#include <future>
#include <iostream>
int find_the_answer_to_ltuae();
void do_other_stuff();
int main()
{
std::future<int> the_answer=std::async(find_the_answer_to_ltuae);
do_other_stuff();
std::cout<<"The answer is "<<the_answer.get()<<std::endl;
}
The calculation is started before `do_other_stuff()`; `the_answer.get()` retrieves its result when it is needed. section 4.2.1
The callable can be a function, function object, or member function. Pass its arguments after the callable; for a member function, first provide the object to invoke it on. `std::ref` passes an object by reference, while move-only callables and arguments can be moved into the task. section 4.2.1
Choose a launch policy when scheduling matters:
- `std::launch::async` requests asynchronous execution. section 4.2.1
- `std::launch::deferred` postpones execution until the future is waited for; `wait()` can therefore run the task. section 4.2.1
- Without an explicit policy, the implementation may start a new thread or defer execution. Do not assume it will automatically limit the number of threads in a recursive algorithm. section 4.2.1 section 4.4.1
Parallel quicksort
Sort one partition with `std::async` while sorting the other through a direct recursive call. Calling `get()` supplies the first partition’s sorted list before the lists are assembled. Repeated recursion creates more tasks, but whether the default policy turns them into new threads is implementation-dependent. section 4.4.1
Listing 4.13. Parallel Quicksort using futures
template<typename T>
std::list<T> parallel_quick_sort(std::list<T> input)
{
if(input.empty())
{
return input;
}
std::list<T> result;
result.splice(result.begin(),input,input.begin());
T const& pivot=*result.begin();
auto divide_point=std::partition(input.begin(),input.end(),
[&](T const& t){return t<pivot;});
std::list<T> lower_part;
lower_part.splice(lower_part.end(),input,input.begin(),
divide_point);
std::future<std::list<T> > new_lower( #1
std::async(¶llel_quick_sort<T>,std::move(lower_part)));
auto new_higher(
parallel_quick_sort(std::move(input))); #2
result.splice(result.end(),new_higher); #3
result.splice(result.begin(),new_lower.get()); #4
return result;
}
!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":15,\"ch\":68},{\"line\":15,\"ch\":70}],[{\"line\":18,\"ch\":68},{\"line\":18,\"ch\":70}],[{\"line\":19,\"ch\":68},{\"line\":19,\"ch\":70}],[{\"line\":20,\"ch\":68},{\"line\":20,\"ch\":70}]]"}
!@%STYLE%@!
The listing shows the asynchronous lower partition, directly computed upper partition, and `get()` at the point where the lower result is assembled. section 4.4.1
If the callable throws, the future becomes ready with the exception instead of a value; `get()` rethrows it. For a task started on a thread by `std::async`, destroying its future without waiting also waits for that thread to finish. That behavior helps prevent a parallel algorithm from leaving work running after an exception, though an unexpectedly discarded future may block. section 4.2.4 section 8.4.1
