En fait, l'exemple que vous venez de donner montre les différences si vous utilisez une fonction assez longue, telle que
//! sleeps for one second and returns 1
auto sleep = [](){
std::this_thread::sleep_for(std::chrono::seconds(1));
return 1;
};
Tâche packagée
A packaged_taskne démarre pas tout seul, vous devez l'invoquer:
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
D'autre part, std::asyncavec launch::asyncessaiera d'exécuter la tâche dans un thread différent:
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";
Inconvénient
Mais avant d'essayer de l'utiliser asyncpour tout, gardez à l'esprit que le futur retourné a un état partagé spécial, qui exige que cela future::~futurebloque:
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;
*/
Donc, si vous voulez un vrai asynchrone, vous devez conserver le retour future, ou si vous ne vous souciez pas du résultat si les circonstances changent:
{
auto pizza = std::async(get_pizza);
/* ... */
if(need_to_go)
return; // ~future will block
else
eat(pizza.get());
}
Pour plus d'informations à ce sujet, consultez l'article de Herb Sutter asyncet~future , qui décrit le problème, et Scott Meyer std::futuresde std::asyncne sont pas spéciaux , qui décrit les idées. Notez également que ce comportement a été spécifié dans C ++ 14 et versions ultérieures , mais aussi couramment implémenté dans C ++ 11.
Autres différences
En utilisant, std::asyncvous ne pouvez plus exécuter votre tâche sur un thread spécifique, où std::packaged_taskpeut être déplacé vers d'autres threads.
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";
De plus, a packaged_taskdoit être invoqué avant d'appeler f.get(), sinon votre programme se figera car l'avenir ne sera jamais prêt:
std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::cout << f.get() << "\n"; // oops!
task(2,3);
TL; DR
À utiliser std::asyncsi vous voulez que certaines choses soient faites et std::packaged_taskque vous ne vous souciez pas vraiment du moment où elles sont terminées, et si vous voulez terminer les choses afin de les déplacer vers d'autres threads ou de les appeler plus tard. Ou, pour citer Christian :
En fin de compte, a std::packaged_taskest juste une fonctionnalité de niveau inférieur à implémenter std::async(c'est pourquoi elle peut faire plus que std::asyncsi elle est utilisée avec d'autres éléments de niveau inférieur, comme std::thread). Simplement parlé a std::packaged_taskest std::functionlié à a std::futureet std::asyncencapsule et appelle a std::packaged_task(éventuellement dans un fil différent).