liveBook · Manning

Std::async

As explained in C++ Concurrency in Action, Second Edition

`std::async` is a C++ standard library function template that launches a function asynchronously or defers its execution and returns a future for its result.

`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(&parallel_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

How C++ Concurrency in Action, Second Edition covers it

How deep it goes
Works through arguments, launch policies, result retrieval, recursive use, and exception safety without detailing an implementation of std::async.
Examples
Uses a background calculation, callable and member-function arguments, parallel quicksort, parallel accumulation, and an exception-throwing square root.
What it assumes
Assumes familiarity with C++ functions and std::thread while introducing future-based result retrieval through examples.
What you can do after
Apply it via std::async to retrieve task results, choose a launch policy, and account for blocking and exceptions.
How it connects
Connects std::future and std::launch to std::thread, std::packaged_task, parallel quicksort, and exception propagation.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of C++ Concurrency in Action, Second Edition. The section links point to the parts of the book it draws on.