Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding continuations with std::future::then

Tags:

c++

c++14

Can anybody explain with an example in C++ what is the difference between async([](){x(); y();}) and async([](){x();}).then([](){y();}) ? My understanding is that in the latter case each of x, y would possibly start in different threads immediately and would only block (in their respective thread) if a get() was invoked on the future passed as input.

like image 965
Martin Avatar asked Jan 29 '16 00:01

Martin


People also ask

What is std :: future in C++?

(since C++11) The class template std::future provides a mechanism to access the result of asynchronous operations: An asynchronous operation (created via std::async, std::packaged_task, or std::promise) can provide a std::future object to the creator of that asynchronous operation.

Is std :: future copyable?

3) std::future is not CopyConstructible.

What is STD promise?

The class template std::promise provides a facility to store a value or an exception that is later acquired asynchronously via a std::future object created by the std::promise object.


2 Answers

... what is the difference between async([](){x(); y();}) and async([](){x();}).then([](){y();})?

Not much really - so why have it then?

In a single word composability. It allows us to construct abstractions based on modular functions (or pieces of functionality) in an asynchronous environment in a expressive manner given the problem domain.

That is not to say the one is right and the other wrong - it just means that those semantics (of .then()) is particularly meaningful when dealing with asynchronous operations.

The language of single threaded computations is something like "do this and do this and do this..." (before we carry on with other things), the language of asynchronous computation is "do this, and when it is done, then do this, and when it is done, then do this..." (while we carry on with other things).

like image 74
Niall Avatar answered Oct 17 '22 16:10

Niall


Such a get() can never block. This is by design in the relevant proposal, N3558.

#include <future>
using namespace std;
int main() {
   future<int> f1 = async([]() { return 123; });

   future<string> f2 = f1.then([](future<int> f) {
        return f.get().to_string(); // here .get() won’t block
   });
}

[..]

Each continuation will not begin until the preceding has completed.

The benefit is that you can now compose several asynchronous operations in an expressive way. Could you just bundle all the code into a single lambda instead? Sure. But ew. Not re-usable and not particularly maintainable either.

Furthermore, there's some exception handling magic done for you. I suggest reading the proposal that defines what std::future::then will actually do, though I admit that there's not much in the way of a "Rationale" section in there: it seems to focus on the downsides of blocking your main thread waiting for a future to complete, and doesn't mention your stated alternative use case. Well, maybe that's the problem: this feature isn't very much designed to replace it. That's why you're struggling to find the functional differences.

In asynchronous programming, it is very common for one asynchronous operation, on completion, to invoke a second operation and pass data to it. The current C++ standard does not allow one to register a continuation to a future. With .then, instead of waiting for the result, a continuation is “attached” to the asynchronous operation, which is invoked when the result is ready. Continuations registered using the .then function will help to avoid blocking waits or wasting threads on polling, greatly improving the responsiveness and scalability of an application.

like image 30
Lightness Races in Orbit Avatar answered Oct 17 '22 15:10

Lightness Races in Orbit