Actually the example you just gave shows the differences if you use a rather long function, such as
//! sleeps for one second and returns 1
auto sleep = [](){
std::this_thread::sleep_for(std::chrono::seconds(1));
return 1;
};
Packaged task
A packaged_task
won't start on its own, you have to invoke it:
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
On the other hand, std::async
with launch::async
will try to run the task in a different thread:
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";
Drawback
But before you try to use async
for everything, keep in mind that the returned future has a special shared state, which demands that future::~future
blocks:
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;
*/
So if you want real asynchronous you need to keep the returned future
, or if you don't care for the result if the circumstances change:
{
auto pizza = std::async(get_pizza);
/* ... */
if(need_to_go)
return; // ~future will block
else
eat(pizza.get());
}
For more information on this, see Herb Sutter's article async
and ~future
, which describes the problem, and Scott Meyer's std::futures
from std::async
aren't special, which describes the insights. Also do note that this behavior was specified in C++14 and up, but also commonly implemented in C++11.
Further differences
By using std::async
you cannot run your task on a specific thread anymore, where std::packaged_task
can be moved to other 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";
Also, a packaged_task
needs to be invoked before you call f.get()
, otherwise you program will freeze as the future will never become ready:
std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::cout << f.get() << "\n"; // oops!
task(2,3);
TL;DR
Use std::async
if you want some things done and don't really care when they're done, and std::packaged_task
if you want to wrap up things in order to move them to other threads or call them later. Or, to quote Christian:
In the end a std::packaged_task
is just a lower level feature for implementing std::async
(which is why it can do more than std::async
if used together with other lower level stuff, like std::thread
). Simply spoken a std::packaged_task
is a std::function
linked to a std::future
and std::async
wraps and calls a std::packaged_task
(possibly in a different thread).