Could someone explain to me the operation of std::experimental::future? I read about it in C++ Concurrency in Action, 2e but the author's example doesn't really make sense to me. I don't understand how a "continuation" works or what the "then()" member function does. An example that uses it would also be nice.
Futures, also known as promises, are a bit of syntactic sugar on top of thread join. The idea is that a caller thread calls a function that starts a worker thread (or adds a task to a queue to be processed by a thread pool or whatever) and the function returns a future<Result>. The caller thread can store the future for later and continue working on other things. When the caller thread actually needs the value from the worker, it can simply call future<Result>::get() (or whatever) and this synchronizes the worker and caller and retrieves the result from the worker.
then() is basically function composition. If you've ever used a functional language, it's equivalent to the dot operator to compose functions. future<Result>::then() accepts a Result2(Result) callable as an argument and returns a future<Result2>, which will later return the result of taking the result of the first future and applying to it the callable passed to then().
In other words,
Alright, but what is the difference between doing that and just having a regular std::future (and not a std::experimental::future) carry that result? For example:
1 2 3 4
auto the_future = std::async([](){return std::to_string(42 * 2);});
std::cout << the_future.get();
Is there any performance benefit to using std::experimental::future over std::future, and if so, what scenarios would you use it in?
Is there actually performance benefits or is it merely for a cleaner appearance and maintainability of code that uses multiple chained operations? I mean, you could do all the operations inside of a lambda with a regular future instead of using std::experimental::future::then
For instance, you might want to do something like this:
1 2 3 4 5 6 7 8 9
std::string transform(const std::string &);
std::vector<future<std::string>> results;
for (auto i : list){
auto f = future<std::string>([](){ return"foo"; });
for (auto j = i; j--;)
f = f.then(transform);
results.emplace_back(std::move(f));
}
As a simple static function this would be annoying to express and might result in a stack overflow.
OK, just a quick question in your code. I know you said this is pseudocode, but would this:
auto f = future<std::string>([](){ return"foo"; });
translate into this
auto f = std::async([]() -> std::string { return"foo"; });
In terms of ACTUAL C++ code?
Also, what is "list" here: for (auto i : list)
And how can you use operator-- on said list objects? for (auto j = i; j--;)
I think instead of "list" you meant to type "results" right?
But if that's the case, then what does j-- do, given that j is of type std::experimental::future? std::experimental::future does not have an operator--(). What exactly is that last for loop doing?
For your first question, no idea. I've never used the standard concurrency classes.
For your second question, list is supposed to be a sequence of integers. for (auto j = i; j--;) runs the body of the loop i times, with j running in reverse from i - 1 to 0 (value inside the body). This form of the loop is useful when you don't care about the value of the index but you want to preserve the type of the loop count while saving some typing.
Well, in the particular case of futures/promises, there's the implementation in Qt. In C# they're called Tasks, and in JavaScript's async-await mechanism promises feature prominently, only they're concurrent but not parallel. That is, JS functions run interleaved but synchronously in a single thread, while external code runs asynchronously.