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 future
s with different values via promise
s, 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.