0

std::future, std::promise and std::threads

I am trying to get my head around a few of the newer C++ concepts such as std::future, std::promise and std::async.

I think I understand pretty well how futures and promises work. They are a apir of classes/objects which can be used to pass data to and from threads in a convenient manner. In summary they work like this:

  • Create a promise defining the type of data to be returned
  • Create a future for the created promise using promise.get_future()
  • Launch a thread, moving the promise into the thread function
  • Parent thread continues to do other work until result from launched thread function is required, at which point parent waits for result by calling future.get().
  • If result has already been "set", parent thread continues, else it goes to sleep for an indefinite/infinite/unlimited (?) time
  • If result has not been "set", child thread continues working
  • Child thread sets the promise value using promise.set_value()
  • This prompts parent thread to wake up if it was sleeping
  • Typically child thread would then exit, parent thread would call thread.join() and continue working

(One of my favorite examples can be found here: https://www.modernescpp.com/index.php/promise-and-future)

Therefore the promise future construct provides a way to launch a thread and return a result while keeping the parent and child threads synchronized at the points in program execution when this is required.

std::async

Here is where I get confused. I have read several documentation pages regarding std::async. What confuses me is that I usually associate future with promise. But when using std::async there is no mention of a promise at any point. Is it hidden by the implementation?

Here's a selection of documentation pages:

As far as I can tell, std::async does the following:

  • Launches a thread, returning a std::future object
  • (Has different launch modes, can be "deferred" meaning the thread does not start work until the result is requested, and "async" meaning the thread starts work immediatly)
  • When the result is required, future.get() is called.
  • Since there is no promise involved in this process, I would have assumed that it is not possible to return a value from a promise using future.get()...
  • However, some examples indicated that the return type is defined by the function to be called with async. The value then is returned by future.get(). So it is possible to return a value, but no std::promise in sight?

Edit: Here's another article which gives details of futures, promises, async and also packaged tasks. It provides some explanations as to why you might use different options, but at the end states the difference between async and launching a thread with future/promise is that the returned value can be set anywhere within the thread function execution rather than just at the end by a return statement.

https://ncona.com/2018/02/futures-async-packaged_tasks-and-promises-in-c/

Questions

  • Why use async instead of a future promise pair?
  • Is async just syntactic sugar for launching a thread while hiding the promise from view of the programmer?

In other words, my question is what is the difference between using async to launch a seperate thread and doing it by explicitly using std::thread with promise and future.

  • Are they functionally, the same?
FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225

1 Answers1

1

Why use async instead of a future promise pair?

Because it is one thing, instead of three + orchestration. Or because your implementation does it with a thread pool, rather than a new thread each time. Or because you want deferred or implementation-defined launch policy.

Is async just syntactic sugar for launching a thread while hiding the promise from view of the programmer?

On some implementations, yes. On others it can run the function on a thread-pool thread. It just has to ensure that reachable thread_local data is fresh for each invocation.

Are they functionally, the same?

In the sense that

template <typename F, typename... Args>
std::future<std::invoke_result_t<F, Args...>> async(F f, Args... args) {
    std::promise<std::invoke_result_t<F, Args...>> prom;
    auto fut = prom.get_future();
    std::thread([p = std::move(prom)](F f, Args... args) { p.set_value_at_thread_exit(f(args...)); }, f, args...).detach();
    return fut;
}

is approximately what std::async does?

Caleth
  • 52,200
  • 2
  • 44
  • 75