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?
- 22,602
- 44
- 128
- 244
- 591
- 1
- 5
- 4
2 Answers
std::async
std::async is a neat and easy way to get a std::future, but:
It does not always start a new thread; the enum value
std::launch::asynccan be passed as the first argument tostd::asyncin order to ensure that a new thread is created to execute the task specified byfunc, thus ensuring thatfuncexecutes asynchronously.auto f = std::async( std::launch::async, func );The destructor of
std::futurecan block until the new thread completes.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::futureis stored in a temporary object, thestd::asynccall will block at the point of the object's destruction, so the following block will take 10 seconds if you remove theauto f =initializations. It will only block for 5 seconds otherwise, because the two sleeps will be concurrent, with a wait for both to complete resulting from the destruction of the two objects at the end of the block: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
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
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:
Use
std::asynconly for simple things, e.g. to make some call non-blocking, but bear in mind the comments on blocking above.Use
std::packaged_taskto easily get to astd::future, and run it as a separate threadstd::thread{ std::move(package), param }.detach();
or
std::thread t { std::move(package), param };
- Use
std::promisewhen you need more control over the future.
See also std::shared_future and on passing exceptions between threads std::promise::set_exception
- 71,784
- 24
- 131
- 181
- 6,060
- 3
- 32
- 53
A promise is used to store a value that was calculated using e.g. a std::async. See http://en.cppreference.com/w/cpp/thread/promise
I can imagine you wonder about the difference between std::packaged_task and std::async (in the most common approach, std::async starts a separate thread NOW to run function/lambda/etc with a (likely) expensive calculation. A std::packaged_task is used to wrap a function/lambda/etc with the current values of arguments so you can LATER run it, either synchronously or in a separate thread).
Both std::packaged_task and std::async provide a std::future that will contain the RESULT of the wrapped function/lambda/etc once run. Internally, the std::future uses a std::promise to hold that result.
- 1,105
- 8
- 22
-
2`std::future` and `std::promise` have a "shared state", so result is not stored in promise object, but in the shared state – user2622016 Jun 10 '14 at 17:37
-
1`packaged_task` does not package values of arguments, it holds only a function object and a "shared state". – user2622016 Jun 10 '14 at 17:41