Na verdade, o exemplo que você deu mostra as diferenças se você usar uma função bastante longa, como
//! sleeps for one second and returns 1
auto sleep = [](){
std::this_thread::sleep_for(std::chrono::seconds(1));
return 1;
};
Tarefa empacotada
A packaged_task
não inicia por si própria, você deve invocá-la:
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
Por outro lado, std::async
com launch::async
tentará executar a tarefa em um thread diferente:
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";
Recua
Mas antes de tentar usar async
para tudo, tenha em mente que o futuro retornado tem um especial compartilhada estado, que exige que os future::~future
blocos:
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;
*/
Portanto, se você quiser real assíncrono, precisará manter o retorno future
, ou se não se importar com o resultado, se as circunstâncias mudarem:
{
auto pizza = std::async(get_pizza);
/* ... */
if(need_to_go)
return; // ~future will block
else
eat(pizza.get());
}
Para mais informações sobre este assunto, veja o artigo de Herb Sutter async
e~future
, que descreve o problema e Scott Meyer std::futures
de std::async
não são especiais , que descreve os insights. Observe também que esse comportamento foi especificado no C ++ 14 e posterior , mas também comumente implementado no C ++ 11.
Diferenças adicionais
Ao usar, std::async
você não pode mais executar sua tarefa em um encadeamento específico, onde std::packaged_task
pode ser movido para outros encadeamentos.
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";
Além disso, é packaged_task
necessário chamar antes de ligar f.get()
, caso contrário, o programa será congelado, pois o futuro nunca ficará pronto:
std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::cout << f.get() << "\n"; // oops!
task(2,3);
TL; DR
Use std::async
se você quiser fazer algumas coisas e realmente não se importa quando elas são feitas, e std::packaged_task
se você deseja agrupar as coisas para movê-las para outros segmentos ou chamá-las mais tarde. Ou, para citar Christian :
No final, a std::packaged_task
é apenas um recurso de nível inferior para implementação std::async
(e é por isso que ele pode fazer mais do que std::async
se usado em conjunto com outras coisas de nível inferior, como std::thread
). Simplesmente falado a std::packaged_task
é um std::function
vinculado a std::future
e std::async
envolve e chama um std::packaged_task
(possivelmente em um segmento diferente).