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?
When to use promise over async or packaged_task?
11.7k Views Asked by Domingos Martins AtThere are 2 best solutions below
On
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.
std::async
std::asyncis a neat and easy way to get astd::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.The destructor of
std::futurecan block until the new thread completes.Normally we expect that only
.get()or.wait()blocks, but for astd::futurereturned fromstd::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:std::packaged_task
std::packaged_taskby itself has nothing to do with threads: it is just a functor and a relatedstd::future. Consider the following:Here we just run the task by
package(1), and after it returns,fis ready so no blocking on.get().There is a feature of
std::packaged_taskthat makes it very useful for threads. Instead of just a function, you can initializestd::threadwith astd::packaged_taskwhich gives a really nice way of getting to the 'std::future'. Consider the following:Because
std::packaged_taskis not copyable, you must move it to new thread withstd::move.std::promise
std::promiseis a powerful mechanism. For example, you can pass a value to new thread without need of any additional synchronization.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 threador
std::promisewhen you need more control over the future.See also
std::shared_futureand on passing exceptions between threadsstd::promise::set_exception