Search code examples
c++c++11promisefutureshared

When do we need std::shared_future instead of std::future for inter-thread synchronization?


I tried to test how std::shared_future is shared between different threads as these threads all calls its wait() function, and wake up after its signal is called. As below:

#include <iostream>
#include <future>
using namespace std;
int main() {
    promise<void> p, p1, p2;
    auto sf = p.get_future().share(); // This line
    auto f1 = [&]() {
        p1.set_value();
        sf.wait();
        return 1;
    };
    auto f2 = [&]() {
        p2.set_value();
        sf.wait();
        return 2;
    };
    auto ret1 = async(launch::async, f1);
    auto ret2 = async(launch::async, f2);
    p1.get_future().wait();
    p2.get_future().wait();
    p.set_value();
    cout << ret1.get() << ", " << ret2.get();
    return 0;
}

The program prints 1, 2 and works fine.

Then I changed the line of auto sf = p.get_future().share(); into auto sf = p.get_future() using oridinay future object, not the shared version, compile and run. I got the same result: while I expected that for the non-shared version, only 1 thread will successfully wait and return while other threads will hang. But seems still the program runs OK.

So my question is: when do we need to use std::shared_future instead of std::future? Or it's just an object like std::shared_ptr, as a simple wrapper of std::future so that it could be passed around?

I mean is there any case that non-shared future doesn't fulfill the need or scenario. Would you help to explain?


Solution

  • The "shared" part of shared_future is not about the waiting but getting.

    I expected that for the non-shared version, only 1 thread will successfully wait and return while other threads will hang.

    No, this is completely safe, you can wait on a future from as many threads as you want (it is a const member, hence thread-safe) and all must unblock when the result is set. But be warned that wait() cannot be called after someone called get().

    The difference is in how you get the results. Remember, std::future stands for a future result set by std::promise.

    • std::future::get() returns by value. It can only be called once and thus only from one thread.
    • std::shared_future::get() returns a const reference. It can be called many times from multiple threads. Of course be careful about the thread-safety of the underlying object - whether its methods are really thread-safe.

    Furthermore std::shared_future can be cloned and multiple such objects can refer to a single shared state, i.e. linked to a single promised object. The shared state exists as long as some future/promise points to it, like std::shared_ptr<State>.

    In your case, you are slightly misusing std::shared_future sf, each thread that awaits the result should get its own clone. That way, its lifetime is safe. The envisioned workflow is:

    • std::promise is created, the [first] future is obtained from it.
    • The promise is given to the producer, consumers do not know about it.
    • The future is given to [each] consumer [which can clone it and pass it along if necessary].

    I mean is there any case that non-shared future doesn't fulfill the need or scenario. Would you help to explain?

    Having two consumer threads, both awaiting the result. std::future would require for exactly one thread to call get and somehow share that result with the other. Although both could have called wait(). On the other hand std::shared_future allows both to "view" the result since it is const. Yes, one has to copy the result if it needs to be passed around but that is unavoidable anyway.