Когда использовать обещание поверх async или packaged_task?

Когда я должен использовать std::promise над std::async или же std::packaged_task?
Можете ли вы привести практические примеры использования каждого из них?

31

Решение

станд :: асинхронной

std::async это аккуратный и простой способ получить std::future, но:

  • Не всегда начинается новый поток; проходить std::launch::async в качестве первого параметра, чтобы заставить его.

    auto f = std::async( std::launch::async, func );
    
  • std::~future деструктор Можно блокировать, пока не закончится новая тема

    auto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); };
    
    {
    auto f = std::async( std::launch::async, sleep, 5 );
    }
    

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

  • если std::future хранится во временном объекте жизни, std::async вызов будет заблокирован на месте, поэтому следующий блок будет принимать 10 секунд если вы удалите auto f = инициализация:

    auto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); };
    
    {
    auto f1 = std::async( std::launch::async, sleep, 5 );
    auto f2 = std::async( std::launch::async, sleep, 5 );
    }
    

станд :: packaged_task

std::packaged_task сам по себе не имеет ничего общего с потоками: это просто функтор и связанное с ним будущее. Учтите следующее:

auto task = [](int i) { std::this_thread::sleep_for(std::chrono::seconds(5)); return i+100; };

std::packaged_task< int(int) > package{ task };
std::future<int> f = package.get_future();
package(1);
std::cout << f.get() << "\n";

Здесь мы просто запускаем задачу package(1)и после того, как он возвращается f готов, так что нет блокировки на .get(),

Но: одна особенность делает packaged_task очень полезным для потоков. Вместо просто функции вы можете инициализировать std::thread с упакованной задачей. Обратите внимание на следующее — packaged_task — это действительно хороший способ получить будущее:

std::packaged_task< int(int) > package{ task };
std::future<int> f = package.get_future();
std::thread t { std::move(package), 5 };

std::cout << f.get() << "\n";       //block here until t finishes

t.join();

std::packaged_task не копируется, поэтому вы перемещаете его в новое задание с помощью std::move,

станд :: обещание

std::promise это мощный механизм, например, вы можете передать значение в новый поток без необходимости какого-либо дополнительного механизма синхронизации.

auto task = [](std::future<int> i) {
std::cout << i.get() << std::flush;
};

std::promise<int> p;
std::thread t{ task, p.get_future() };

std::this_thread::sleep_for(std::chrono::seconds(5));
p.set_value(5);

t.join();

Новая тема будет ждать нас на .get()


Итак, в общем, отвечая на ваш вопрос:

  • использование std::async только для простых вещей, например чтобы сделать некоторые звонки неблокирующими, но имейте в виду комментарии о блокировке выше.
  • использование std::packaged_task легко получить будущее и запустить его как отдельный поток

    std::thread{ std::move(package), param }.detach();
    

    или же

    std::thread t { std::move(package), param };
    
  • использование std::promise когда вам нужно больше контроля над будущим.

Смотрите также std::shared_future и при передаче исключений между потоками std::promise::set_exception

37

Другие решения

Обещание используется для хранения значения, которое было вычислено с использованием, например, std :: async.
Увидеть http://en.cppreference.com/w/cpp/thread/promise

Я могу представить, что вас интересует разница между std :: packaged_task и std :: async (в наиболее распространенном подходе std :: async запускает отдельный поток СЕЙЧАС для запуска функции / lambda / etc с (вероятно) дорогостоящим вычислением.
Std :: packaged_task используется, чтобы обернуть функцию / лямбда / и т. Д. Текущими значениями аргументов, чтобы вы могли ПОЗЖЕ запустить ее, либо синхронно, либо в отдельном потоке).

И std :: packaged_task, и std :: async предоставляют std :: future, который будет содержать RESULT обернутой функции / lambda / etc после запуска.
Внутренне std :: future использует std :: обещание для хранения этого результата.

-1

По вопросам рекламы [email protected]