Futuros contra promesas

135

Me estoy confundiendo con la diferencia entre un futuro y una promesa.

Obviamente, tienen diferentes métodos y otras cosas, pero ¿cuál es el caso de uso real?

¿Lo es?:

  • cuando estoy administrando alguna tarea asincrónica, uso el futuro para obtener el valor "en el futuro"
  • cuando soy la tarea asíncrona, uso promesas como tipo de retorno para permitir que el usuario obtenga un futuro de mi promesa
Šimon Tóth
fuente
1
Escribí un poco sobre esto en esta respuesta .
Kerrek SB
1
posible duplicado de ¿Qué es std :: promise?
Nicol Bolas

Respuestas:

163

Futuro y promesa son los dos lados separados de una operación asincrónica.

std::promise es utilizado por el "productor / escritor" de la operación asincrónica.

std::future es utilizado por el "consumidor / lector" de la operación asincrónica.

La razón por la que se separa en estas dos "interfaces" separadas es para ocultar la funcionalidad "escribir / configurar" del "consumidor / lector".

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

Una forma (incompleta) de implementar std :: async usando std :: promise podría ser:

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

El uso de std::packaged_taskwhich es un ayudante (es decir, básicamente hace lo que estábamos haciendo anteriormente) a su alrededor std::promisepodría hacer lo siguiente, que es más completo y posiblemente más rápido:

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

Tenga en cuenta que esto es ligeramente diferente de std::asyncdonde el devuelto std::futurecuando se destruye en realidad bloquea hasta que el hilo haya terminado.

ronag
fuente
3
@taras sugiere que regresar std::move(something)es inútil y también perjudica a (N) RVO. Revertiendo su edición.
polkovnikov.ph
En Visual Studio 2015, utilice std :: cout << future.get (). C_str ();
Damian
66
Para aquellos que todavía están confundidos, vea esta respuesta .
kawing-chiu
2
Esa es una vez un productor - consumidor, en mi humilde opinión, que no es realmente un patrón productor - consumidor.
Martin Meeser