Фьючерсы против обещаний


135

Я путаю себя с разницей между будущим и обещанием.

Очевидно, у них разные методы и прочее, но каков фактический вариант использования?

Это?:

  • когда я управляю какой-то асинхронной задачей, я использую future, чтобы получить значение "в будущем"
  • когда я выполняю асинхронную задачу, я использую обещание в качестве возвращаемого типа, чтобы позволить пользователю получить будущее из моего обещания

1
Я немного писал об этом в этом ответе .
Kerrek SB

Ответы:


163

Future и Promise - это две разные стороны асинхронной операции.

std::promise используется «производителем / писателем» асинхронной операции.

std::future используется «потребителем / читателем» асинхронной операции.

Причина, по которой он разделен на эти два отдельных «интерфейса», состоит в том, чтобы скрыть функциональность «запись / установка» от «потребителя / читателя».

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

Один (неполный) способ реализовать std :: async с использованием std :: prom может быть:

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

Используя std::packaged_taskкоторый является помощником (то есть он в основном делает то, что мы делали выше), std::promiseвы можете сделать следующее, что будет более полным и, возможно, быстрее:

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

Обратите внимание, что это немного отличается от того, std::asyncгде возвращенный std::futureпри уничтожении будет фактически блокироваться, пока поток не будет завершен.


3
@taras предполагает, что возвращение std::move(something)бесполезно, а также вредит (N) RVO. Отмена его редактирования.
polkovnikov.ph

В Visual Studio 2015 используйте std :: cout << future.get (). C_str ();
Damian

6
Для тех, кто все еще не понимает, см. Этот ответ .
kawing-chiu

2
Это разовый производитель-потребитель, ИМХО, это не совсем модель производитель-потребитель.
Мартин Мизер,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.