Tatsächlich zeigt das Beispiel, das Sie gerade gegeben haben, die Unterschiede, wenn Sie eine ziemlich lange Funktion verwenden, wie z
//! sleeps for one second and returns 1
auto sleep = [](){
std::this_thread::sleep_for(std::chrono::seconds(1));
return 1;
};
Gepackte Aufgabe
A packaged_task
startet nicht von alleine, Sie müssen es aufrufen:
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
Auf der anderen Seite wird std::async
with launch::async
versuchen, die Aufgabe in einem anderen Thread auszuführen:
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";
Nachteil
Bevor Sie jedoch versuchen, async
alles zu verwenden, denken Sie daran, dass die zurückgegebene Zukunft einen speziellen gemeinsamen Zustand hat, der Folgendes erfordert 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;
*/
Wenn Sie also wirklich asynchron sein möchten, müssen Sie die Rückgabe beibehalten future
, oder wenn Sie sich nicht für das Ergebnis interessieren, wenn sich die Umstände ändern:
{
auto pizza = std::async(get_pizza);
/* ... */
if(need_to_go)
return; // ~future will block
else
eat(pizza.get());
}
Weitere Informationen zu diesem Thema finden Herb Sutter Artikel async
und~future
, die das Problem beschreibt, und Scott Meyers std::futures
von std::async
nicht speziell sind , die die Erkenntnisse beschreibt. Beachten Sie auch, dass dieses Verhalten in C ++ 14 und höher angegeben , aber auch häufig in C ++ 11 implementiert wurde.
Weitere Unterschiede
Mit können std::async
Sie Ihre Aufgabe nicht mehr für einen bestimmten Thread ausführen, der in std::packaged_task
andere Threads verschoben werden kann.
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";
Außerdem muss ein packaged_task
aufgerufen werden, bevor Sie aufrufen f.get()
, da sonst Ihr Programm einfriert, da die Zukunft niemals bereit wird:
std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::cout << f.get() << "\n"; // oops!
task(2,3);
TL; DR
Verwenden std::async
Sie diese Option, std::packaged_task
wenn Sie möchten, dass einige Dinge erledigt werden und es Ihnen egal ist, wann sie erledigt sind, und wenn Sie Dinge einpacken möchten, um sie in andere Threads zu verschieben oder später aufzurufen. Oder um Christian zu zitieren :
Am Ende std::packaged_task
ist a nur eine Funktion auf niedrigerer Ebene für die Implementierung std::async
(weshalb es mehr kann, als std::async
wenn es zusammen mit anderen Dingen auf niedrigerer Ebene verwendet wird std::thread
). Einfach gesprochen std::packaged_task
ist a ein std::function
Link zu a std::future
und std::async
umschließt und ruft a auf std::packaged_task
(möglicherweise in einem anderen Thread).