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_taskstartet 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::asyncwith launch::asyncversuchen, 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, asyncalles 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 asyncund~future , die das Problem beschreibt, und Scott Meyers std::futuresvon std::asyncnicht 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::asyncSie Ihre Aufgabe nicht mehr für einen bestimmten Thread ausführen, der in std::packaged_taskandere 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_taskaufgerufen 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::asyncSie diese Option, std::packaged_taskwenn 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_taskist a nur eine Funktion auf niedrigerer Ebene für die Implementierung std::async(weshalb es mehr kann, als std::asyncwenn es zusammen mit anderen Dingen auf niedrigerer Ebene verwendet wird std::thread). Einfach gesprochen std::packaged_taskist a ein std::functionLink zu a std::futureund std::asyncumschließt und ruft a auf std::packaged_task(möglicherweise in einem anderen Thread).