Futures vs. Versprechen

134

Ich verwechsle mich mit dem Unterschied zwischen einer Zukunft und einem Versprechen.

Natürlich haben sie unterschiedliche Methoden und Dinge, aber was ist der eigentliche Anwendungsfall?

Ist es?:

  • Wenn ich eine asynchrone Aufgabe verwalte, verwende ich future, um den Wert "in future" zu erhalten.
  • Wenn ich die asynchrone Aufgabe bin, verwende ich Versprechen als Rückgabetyp, damit der Benutzer eine Zukunft aus meinem Versprechen ziehen kann
Šimon Tóth
quelle
1
Ich habe in dieser Antwort ein wenig darüber geschrieben .
Kerrek SB
1
mögliches Duplikat von Was ist std :: Versprechen?
Nicol Bolas

Antworten:

162

Future und Promise sind die beiden getrennten Seiten einer asynchronen Operation.

std::promise wird vom "Produzenten / Schreiber" der asynchronen Operation verwendet.

std::future wird vom "Consumer / Reader" der asynchronen Operation verwendet.

Der Grund, warum es in diese beiden separaten "Schnittstellen" unterteilt ist, besteht darin , die "Schreiben / Festlegen" -Funktionalität vor dem "Verbraucher / Leser" zu verbergen .

auto promise = std::promise<std::string>();

auto producer = std::thread([&]
{
    promise.set_value("Hello World");
});

auto future = promise.get_future();

auto consumer = std::thread([&]
{
    std::cout << future.get();
});

producer.join();
consumer.join();

Eine (unvollständige) Möglichkeit, std :: async mit std :: versprechen zu implementieren, könnte sein:

template<typename F>
auto async(F&& func) -> std::future<decltype(func())>
{
    typedef decltype(func()) result_type;

    auto promise = std::promise<result_type>();
    auto future  = promise.get_future();

    std::thread(std::bind([=](std::promise<result_type>& promise)
    {
        try
        {
            promise.set_value(func()); // Note: Will not work with std::promise<void>. Needs some meta-template programming which is out of scope for this question.
        }
        catch(...)
        {
            promise.set_exception(std::current_exception());
        }
    }, std::move(promise))).detach();

    return std::move(future);
}

Wenn Sie std::packaged_taskeinen Helfer verwenden (dh im Grunde das tun, was wir oben getan haben), können std::promiseSie Folgendes tun, was vollständiger und möglicherweise schneller ist:

template<typename F>
auto async(F&& func) -> std::future<decltype(func())>
{
    auto task   = std::packaged_task<decltype(func())()>(std::forward<F>(func));
    auto future = task.get_future();

    std::thread(std::move(task)).detach();

    return std::move(future);
}

Beachten Sie, dass dies etwas anders ist, als std::asyncwenn der zurückgegebene std::futureWille, wenn er zerstört wird, tatsächlich blockiert, bis der Thread fertig ist.

Ronag
quelle
3
@taras schlägt vor, dass die Rückkehr std::move(something)nutzlos ist und auch (N) RVO schmerzt. Zurücksetzen seiner Bearbeitung.
polkovnikov.ph
In Visual Studio 2015 verwenden Sie bitte std :: cout << future.get (). C_str ();
Damian
6
Für diejenigen, die immer noch verwirrt sind, siehe diese Antwort .
Kawing-Chiu
2
Das ist ein einmaliger Produzent - Konsument, IMHO, der eigentlich kein Produzent - Konsumentenmuster ist.
Martin Meeser