I am trying to establish some heuristics to help me decide the appropriate std::thread class to use.
As I understand it, from highest level (simplest to use, but least flexible) to lowest level, we have:
I think I have a decent grasp of when to use the first two, but am still unclear about std::promise.
std::future in conjunction with a std::async call, effectively transforms a producing callback/functor/lambda to an asynchronous call (which returns immediately, by definition). A singular consumer can call std::future::get(), a blocking call, to get its results back.
std::shared_future is just a version which allows for multiple consumers.
If you want to bind a std::future value with a producer callback, but want to defer the actual invocation to a later time (when you associate the task to a spawning thread), std::packaged_task is the right choice. But now, since the corresponding std::future to the std::package_task could, in the general case, be accessed by multiple-threads, we may have to take care to use a std::mutex. Note that with std::async, in the first case, we don't have to worry about locking.
Having read some interesting links on promise, I think I understand its mechanisms and how to set them up, but my question is, when would you choose to use a promise over the other three?
I'm looking more for an application-level answer, like a rule-of-thumb (fill the ??? in 3. above), as opposed to the answer in the link (eg use std::promise to implement some library mechanism), so I can more easily explain how to choose the proper class to a beginning user of std::thread.
In other words, it would be nice to have an useful example of what I can do with a std::promise that cannot be done with the other mechanisms.
ANSWER
A std::future is a strange beast: in general, you cannot modify its value directly.
Three producers which can modify its value are:
std::async through an asynchronous callback, which will return a std::future instance.std::packaged_task, which, when passed to a thread, will invoke its callback thereby updating the std::future instance associated with that std::packaged_task. This mechanism allows for early binding of a producer, but a later invocation.std::promise, which allows one to modify its associated std::future through its set_value() call. With this direct control over mutating a std::future, we must ensure that that the design is thread-safe if there are multiple producers (use std::mutex as necessitated).I think SethCarnegie's answer:
An easy way to think of it is that you can either set a future by returning a value or by using a promise. future has no set method; that functionality is provided by promise.
helps clarify when to use a promise. But we have to keep in mind that a std::mutex may be necessary, as the promise might be accessible from different threads, depending on usage.
Also, David's Rodriguez's answer is also excellent:
The consumer end of the communication channel would use a std::future to consume the datum from the shared state, while the producer thread would use a std::promise to write to the shared state.
But as an alternative, why not simply just use a std::mutex on a stl container of results, and one thread or a threadpool of producers to act on the container? What does using std::promise, instead, buy me, besides some extra readability vs a stl container of results?
The control appears to be better in the std::promise version:
The following google-test passes both helgrind and drd, confirming that with a single producer, and with the use of wait(), a mutex is not needed.
TEST
static unsigned MapFunc( std::string const& str )
{
if ( str=="one" ) return 1u;
if ( str=="two" ) return 2u;
return 0u;
}
TEST( Test_future, Try_promise )
{
typedef std::map<std::string,std::promise<unsigned>> MAP;
MAP my_map;
std::future<unsigned> f1 = my_map["one"].get_future();
std::future<unsigned> f2 = my_map["two"].get_future();
std::thread{
[ ]( MAP& m )
{
m["one"].set_value( MapFunc( "one" ));
m["two"].set_value( MapFunc( "two" ));
},
std::ref( my_map )
}.detach();
f1.wait();
f2.wait();
EXPECT_EQ( 1u, f1.get() );
EXPECT_EQ( 2u, f2.get() );
}
You don't choose to use a promise instead of the others, you use a promise to fulfill a future in conjunction with the others. The code sample at cppreference.com gives an example of using all four:
#include <iostream>
#include <future>
#include <thread>
int main()
{
// future from a packaged_task
std::packaged_task<int()> task([](){ return 7; }); // wrap the function
std::future<int> f1 = task.get_future(); // get a future
std::thread(std::move(task)).detach(); // launch on a thread
// future from an async()
std::future<int> f2 = std::async(std::launch::async, [](){ return 8; });
// future from a promise
std::promise<int> p;
std::future<int> f3 = p.get_future();
std::thread( [](std::promise<int>& p){ p.set_value(9); },
std::ref(p) ).detach();
std::cout << "Waiting...";
f1.wait();
f2.wait();
f3.wait();
std::cout << "Done!\nResults are: "
<< f1.get() << ' ' << f2.get() << ' ' << f3.get() << '\n';
}
prints
Waiting...Done!
Results are: 7 8 9
Futures are used with all three threads to get their results, and a promise is used with the third one to fulfill a future by means other than a return value. Also, a single thread can fulfill multiple futures with different values via promises, which it can't do otherwise.
An easy way to think of it is that you can either set a future by returning a value or by using a promise. future has no set method; that functionality is provided by promise. You choose what you need based on what the situation allows.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With