Futures vs promesses

135

Je me confond avec la différence entre un avenir et une promesse.

De toute évidence, ils ont des méthodes et des trucs différents, mais quel est le cas d'utilisation réel?

Est-ce?:

  • lorsque je gère une tâche asynchrone, j'utilise future pour obtenir la valeur "in future"
  • lorsque je suis la tâche asynchrone, j'utilise la promesse comme type de retour pour permettre à l'utilisateur d'avoir un avenir grâce à ma promesse
Šimon Tóth
la source
1
J'ai écrit un peu à ce sujet dans cette réponse .
Kerrek SB
1
duplication possible de Qu'est-ce que std :: promise?
Nicol Bolas

Réponses:

163

Future et Promise sont les deux côtés distincts d'une opération asynchrone.

std::promise est utilisé par le "producteur / rédacteur" de l'opération asynchrone.

std::future est utilisé par le "consommateur / lecteur" de l'opération asynchrone.

La raison pour laquelle il est séparé en ces deux "interfaces" séparées est de masquer la fonctionnalité "écriture / définition" du "consommateur / lecteur".

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();

Une façon (incomplète) d'implémenter std :: async en utilisant std :: promise pourrait être:

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);
}

En utilisant std::packaged_taskqui est une aide (c'est-à-dire qu'il fait essentiellement ce que nous faisions ci-dessus) autour de std::promisevous, vous pouvez faire ce qui est plus complet et peut-être plus rapide:

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);
}

Notez que ceci est légèrement différent de l' std::asyncendroit où le std::futuresera retourné une fois détruit se bloquer jusqu'à ce que le thread soit terminé.

Ronag
la source
3
@taras suggère que le retour std::move(something)est inutile et que cela fait également mal au (N) RVO. Revenir à sa modification.
polkovnikov.ph
Dans Visual Studio 2015, utilisez std :: cout << future.get (). C_str ();
Damian
6
Pour ceux qui sont encore confus, voyez cette réponse .
kawing-chiu
2
C'est un producteur - consommateur, à mon humble avis, qui n'est pas vraiment un modèle producteur - consommateur.
Martin Meeser