Насправді наведений вами приклад показує відмінності, якщо ви використовуєте досить довгу функцію, наприклад
//! sleeps for one second and returns 1
auto sleep = [](){
std::this_thread::sleep_for(std::chrono::seconds(1));
return 1;
};
Пакетне завдання
packaged_taskЧи не заводиться на це самостійно, ви повинні посилатися на нього:
std::packaged_task<int()> task(sleep);
auto f = task.get_future();
task(); // invoke the function
// You have to wait until task returns. Since task calls sleep
// you will have to wait at least 1 second.
std::cout << "You can see this after 1 second\n";
// However, f.get() will be available, since task has already finished.
std::cout << f.get() << std::endl;
std::async
З іншого боку, std::asyncс launch::asyncспробує запустити завдання в іншій нитці:
auto f = std::async(std::launch::async, sleep);
std::cout << "You can see this immediately!\n";
// However, the value of the future will be available after sleep has finished
// so f.get() can block up to 1 second.
std::cout << f.get() << "This will be shown after a second!\n";
Недолік
Але перш ніж спробувати використовувати asyncдля всього, майте на увазі, що повернене майбутнє має особливий спільний стан, який вимагає future::~futureблокувати:
std::async(do_work1); // ~future blocks
std::async(do_work2); // ~future blocks
/* output: (assuming that do_work* log their progress)
do_work1() started;
do_work1() stopped;
do_work2() started;
do_work2() stopped;
*/
Тож якщо ви хочете справжнього асинхронного, вам потрібно тримати повернене future, або якщо ви не піклуєтесь про результат, якщо обставини змінюються:
{
auto pizza = std::async(get_pizza);
/* ... */
if(need_to_go)
return; // ~future will block
else
eat(pizza.get());
}
Для отримання додаткової інформації з цього питання див стаття Герба Саттер asyncі~future , який описує проблему, і Скотт Майєр std::futuresз std::asyncне є спеціальними , яка описує розуміння. Також зауважте, що така поведінка була зазначена в C ++ 14 і вище , але також зазвичай реалізована в C ++ 11.
Подальші відмінності
Використовуючи std::asyncви більше не можете виконувати завдання на певній нитці, де std::packaged_taskможна перемістити до інших потоків.
std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::thread myThread(std::move(task),2,3);
std::cout << f.get() << "\n";
Також packaged_taskперед тим, як дзвонити f.get(), потрібно викликати потреби , інакше програма застигне, оскільки майбутнє ніколи не стане готовим:
std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::cout << f.get() << "\n"; // oops!
task(2,3);
TL; DR
Використовуйте, std::asyncякщо ви хочете, щоб якісь речі були зроблені, і вам зовсім не важливо, коли вони будуть виконані, і std::packaged_taskякщо ви хочете завершити їх, щоб перемістити їх в інші теми або зателефонувати їм пізніше. Або, цитувати Крістіана :
Врешті-решт, std::packaged_taskце лише функція нижчого рівня для реалізації std::async(саме тому вона може зробити більше, ніж std::asyncякщо використовувати разом з іншими матеріалами нижчого рівня, як-от std::thread). Просто висловлювалася std::packaged_taskце std::functionпов'язано з std::futureі std::asyncобгорток і називає std::packaged_task(можливо , в іншому потоці).