Когда я должен использовать std::promise
над std::async
или же std::packaged_task
?
Можете ли вы привести практические примеры использования каждого из них?
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 );
}
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
Обещание используется для хранения значения, которое было вычислено с использованием, например, 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 :: обещание для хранения этого результата.