When should I use std::promise
over std::async
or std::packaged_task
?
Can you give me practical examples of when to use each one of them?
std::async
is a neat and easy way to get a std::future
, but:
It does not always it start a new thread; pass std::launch::async
as a first parameter to force it.
auto f = std::async( std::launch::async, func );
The std::~future
destructor can block until the new thread finishes
auto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); };
{
auto f = std::async( std::launch::async, sleep, 5 );
}
Normally we expect that only .get()
or .wait()
blocks, but for a std::future
returned from std::async
, the destructor also may block, so be careful not to block your main thread just by forgetting about it.
If the std::future
is stored in a temporary-life object, the std::async
call will block immediately, so the following block will take 10 seconds if you remove the auto f =
initializations:
auto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); };
{
auto f1 = std::async( std::launch::async, sleep, 5 );
auto f2 = std::async( std::launch::async, sleep, 5 );
}
std::packaged_task
by itself has nothing to do with threads: it is just a functor and a related std::future
. Consider the following:
auto task = [](int i) { std::this_thread::sleep_for(std::chrono::seconds(5)); return i+100; };
std::packaged_task< int(int) > package{ task };
std::future<int> f = package.get_future();
package(1);
std::cout << f.get() << "\n";
Here we just run the task by package(1)
, and after it returns, f
is ready so no blocking on .get()
.
There is a feature of std::packaged_task
that makes it very useful for threads. Instead of just a function, you can initialize std::thread
with a std::packaged_task
which gives a really nice way of getting to the 'std::future'. Consider the following:
std::packaged_task< int(int) > package{ task };
std::future<int> f = package.get_future();
std::thread t { std::move(package), 5 };
std::cout << f.get() << "\n"; //block here until t finishes
t.join();
Because std::packaged_task
is not copyable, you must move it to new thread with std::move
.
std::promise
is a powerful mechanism. For example, you can pass a value to new thread without need of any additional synchronization.
auto task = [](std::future<int> i) {
std::cout << i.get() << std::flush;
};
std::promise<int> p;
std::thread t{ task, p.get_future() };
std::this_thread::sleep_for(std::chrono::seconds(5));
p.set_value(5);
t.join();
New thread will wait for us on .get()
So, in general, answering your question:
std::async
only for simple things, e.g. to make some call non-blocking, but bear in mind the comments on blocking above.Use std::packaged_task
to easily get to a std::future
, and run it as a separate thread
std::thread{ std::move(package), param }.detach();
or
std::thread t { std::move(package), param };
Use std::promise
when you need more control over the future.
See also std::shared_future
and on passing exceptions between threads std::promise::set_exception