Насправді наведений вами приклад показує відмінності, якщо ви використовуєте досить довгу функцію, наприклад
//! 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
(можливо , в іншому потоці).