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_task
ne 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::async
avec launch::async
essaiera 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 async
pour tout, gardez à l'esprit que le futur retourné a un état partagé spécial, qui exige que cela future::~future
bloque:
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 async
et~future
, qui décrit le problème, et Scott Meyer std::futures
de std::async
ne 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::async
vous ne pouvez plus exécuter votre tâche sur un thread spécifique, où std::packaged_task
peut ê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_task
doit ê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::async
si vous voulez que certaines choses soient faites et std::packaged_task
que 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_task
est juste une fonctionnalité de niveau inférieur à implémenter std::async
(c'est pourquoi elle peut faire plus que std::async
si elle est utilisée avec d'autres éléments de niveau inférieur, comme std::thread
). Simplement parlé a std::packaged_task
est std::function
lié à a std::future
et std::async
encapsule et appelle a std::packaged_task
(éventuellement dans un fil différent).