Futuros x promessas

135

Estou me confundindo com a diferença entre um futuro e uma promessa.

Obviamente, eles têm métodos e coisas diferentes, mas qual é o caso de uso real?

É isso?:

  • quando estou gerenciando alguma tarefa assíncrona, uso future para obter o valor "no futuro"
  • quando sou a tarefa assíncrona, uso promessa como o tipo de retorno para permitir que o usuário obtenha um futuro da minha promessa
Šimon Tóth
fonte
1
Eu escrevi um pouco sobre isso nesta resposta .
Kerrek SB 27/09/12
1
possível duplicata de O que é std :: promessa?
Nicol Bolas

Respostas:

163

Futuro e Promessa são os dois lados separados de uma operação assíncrona.

std::promise é usado pelo "produtor / gravador" da operação assíncrona.

std::future é usado pelo "consumidor / leitor" da operação assíncrona.

O motivo pelo qual é separado nessas duas "interfaces" separadas é ocultar a funcionalidade "gravar / definir" do "consumidor / leitor".

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

Uma maneira (incompleta) de implementar std :: async usando std :: promessa pode 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);
}

Usando o std::packaged_taskque é um auxiliar (ou seja, basicamente faz o que estávamos fazendo acima) em torno de std::promisevocê, você pode fazer o seguinte, que é mais completo e possivelmente mais 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);
}

Observe que isso é um pouco diferente de std::asynconde os retornados std::future, quando destruídos, realmente bloqueiam até que o encadeamento seja concluído.

ronag
fonte
3
A @taras sugere que o retorno std::move(something)é inútil e também prejudica o (N) RVO. Revertendo sua edição.
Polkovnikov.ph 30/05
No Visual Studio 2015, use std :: cout << future.get (). C_str ();
Damian
6
Para aqueles que ainda estão confusos, veja esta resposta .
Kawing-chiu 11/08/16
2
Esse é um único produtor - consumidor, IMHO que não é realmente um padrão produtor - consumidor.
Martin Meeser 12/08/16