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).
std::packaged_task
est juste une fonctionnalité de niveau inférieur à implémenterstd::async
(c'est pourquoi elle peut faire plus questd::async
si elle est utilisée avec d'autres éléments de niveau inférieur, commestd::thread
). Simplement parlé astd::packaged_task
eststd::function
lié à astd::future
etstd::async
encapsule et appelle astd::packaged_task
(éventuellement dans un fil différent).Tâche packagée vs asynchrone
p> La tâche packagée contient une tâche
[function or function object]
et une paire future / promesse. Lorsque la tâche exécute une instruction return, elle provoqueset_value(..)
sur lapackaged_task
promesse de.a> Étant donné la tâche Future, Promise et Package, nous pouvons créer des tâches simples sans trop se soucier des threads [le thread est juste quelque chose que nous donnons pour exécuter une tâche].
Cependant, nous devons considérer le nombre de threads à utiliser ou si une tâche est mieux exécutée sur le thread actuel ou sur un autre, etc. Ces décisions peuvent être gérées par un lanceur de thread appelé
async()
, qui décide s'il faut créer un nouveau thread ou recycler un ancien ou exécutez simplement la tâche sur le thread actuel. Il renvoie un avenir.la source
la source