In realtà l'esempio che hai appena mostrato mostra le differenze se usi una funzione piuttosto lunga, come
//! sleeps for one second and returns 1
auto sleep = [](){
std::this_thread::sleep_for(std::chrono::seconds(1));
return 1;
};
Compito impacchettato
A packaged_task
non inizierà da solo, devi invocarlo:
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'altra parte, std::async
con launch::async
proverà a eseguire l'attività in un thread diverso:
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";
Inconveniente
Ma prima di provare a utilizzare async
tutto, tieni presente che il futuro restituito ha uno stato condiviso speciale, che richiede che future::~future
blocchi:
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;
*/
Quindi, se vuoi un vero asincrono, devi conservare il reso future
o se non ti interessa il risultato se le circostanze cambiano:
{
auto pizza = std::async(get_pizza);
/* ... */
if(need_to_go)
return; // ~future will block
else
eat(pizza.get());
}
Per ulteriori informazioni su questo, vedi l'articolo di Herb Sutter async
e~future
, che descrive il problema, e Scott Meyer's std::futures
di std::async
non sono speciali , che descrive le intuizioni. Si noti inoltre che questo comportamento è stato specificato in C ++ 14 e versioni successive , ma anche comunemente implementato in C ++ 11.
Ulteriori differenze
Usando std::async
non è più possibile eseguire l'attività su un thread specifico, dove std::packaged_task
può essere spostato su altri thread.
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";
Inoltre, è packaged_task
necessario invocare un messaggio prima di chiamare f.get()
, altrimenti il programma si bloccherà poiché il futuro non sarà mai pronto:
std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::cout << f.get() << "\n"; // oops!
task(2,3);
TL; DR
Usa std::async
se vuoi che alcune cose vengano fatte e che non ti interessino davvero quando sono fatte, e std::packaged_task
se vuoi concludere le cose per spostarle su altri thread o chiamarle in seguito. Oppure, per citare Christian :
Alla fine a std::packaged_task
è solo una funzionalità di livello inferiore per l'implementazione std::async
(motivo per cui può fare di più che std::async
se usata insieme ad altre cose di livello inferiore, come std::thread
). Semplicemente detto a std::packaged_task
è un std::function
collegato a std::future
e std::async
avvolge e chiama a std::packaged_task
(possibilmente in un thread diverso).