I have a few questions about the implementation of the function then()
in Herb Sutter's talk. This function is used to chain asynchronous operations, the parameter f
is a future from one operation and the parameter w
is the 'work' for this operation (lambda).
template <typename Fut, typename Work> auto then(Fut f, Work w) -> future<decltype(w(f.get()))> { return async([=]{ w(f.get()); }); }
An example of application would be:
std::future<int> f = std::async([]{ std::this_thread::sleep_for(std::chrono::microseconds(200)); return 10; }); auto f2 = then(std::move(f), [](int i){ return 2 * i; });
The main thread spawns the tasks but does not wait for any of them to finish.
Firstly, future<T>
does not have a copy constructor. This means, that the suggested implementation can be only used with shared_future<T>
unless we change the call to async()
to move the future into the lambda. This SO question suggested a way of doing it but it seems too complicated. I re-implemented the function and I am wondering whether my code is correct or whether I missed something...
Secondly, the future that is passed to the then()
function might be void
so we actually need 2 implementations of then()
, right? One for futures returning T
and one for futures returning void
.
Lastly, should the lambda inside the body of then()
not have a return statement so that we can actually return the value back? Without the return statement, then returns future<void>
, right?
I tried to address the above points and this is what I came up with. Is it correct?
template <typename T, typename Work> auto then(future<T> f, Work w) -> future<decltype(w(f.get()))> { return async([](future<T> f, Work w) { return w(f.get()); }, move(f), move(w)); } template <typename Work> auto then(future<void> f, Work w) -> future<decltype(w())> { return async([](future<void> f, Work w) { f.wait(); return w(); }, move(f), move(w)); }
The function template async runs the function f asynchronously (potentially in a separate thread which might be a part of a thread pool) and returns a std::future that will eventually hold the result of that function call. 1) Behaves as if (2) is called with policy being std::launch::async | std::launch::deferred.
A future is an object that can retrieve a value from some provider object or function, properly synchronizing this access if in different threads. "Valid" futures are future objects associated to a shared state, and are constructed by calling one of the following functions: async. promise::get_future.
How does std::launch::async Work in Different Implementations? For now, we know that if no policy is specified, then std::async launches a callable function in a separate thread. However, the C++ standard does not specify whether the thread is a new one or reused from a thread pool.
The problem with this approach to .then() is that you spawn 2 threads (that is costly) simultaneously, and second of them would block on its future.get/wait (if the first one would run long enough, of course) So, its better to use the work queue, to serialize the jobs execution order (and re-cycle the existing threads). Just look for a good Thread pool pattern implementation
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With