25

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:

  1. std::async with/std::future (std::shared_future) (when you want to execute on a one-time throw-away producer-thread async)
  2. std::packaged_task (when you want to assign a producer, but defer the call to the thread)
  3. std::promise (???)

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:

  1. std::async through an asynchronous callback, which will return a std::future instance.
  2. 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.
  3. 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:

  1. wait() will block on a given future until the result is produced
  2. if there is only one producer thread, a mutex is not necessary

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() );
}
Community
  • 1
  • 1
kfmfe04
  • 14,936
  • 14
  • 74
  • 140
  • `std::promise` and `std::future` and `std::thread` are used in conjunction; a `promise` is given by the executing thread and a `future` is taken by the starting thread. – Seth Carnegie Jan 11 '13 at 17:43
  • @SethCarnegie: I'll keep an eye on the answers here then :P – K-ballo Jan 11 '13 at 17:47
  • 2
    Of course a mutex isn't needed for a `std::promise` to provide a result to a `std::future`, **that's the point**, that's why it exists, to replace explicit manual synchronization with mutexes. Simialarly, don't use a mutex to prevent multiple accesses to a `std::future`, use a `std::shared_future` instead. – Jonathan Wakely Jan 13 '13 at 18:43
  • 2
    How come there's an answer in the question? – Lightness Races in Orbit Feb 16 '15 at 19:25

2 Answers2

22

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.

Community
  • 1
  • 1
Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • yes, I understand all this (how to use a promise and its relation to the other classes), but when/why would you use this over the other, simpler-to-use mechanisms? in other words, fill in the (???) in 3. – kfmfe04 Jan 11 '13 at 17:51
  • 1
    @kfmfe04 a `promise` can be used to fulfill a `future` without returning a value from the thread (and thereby stopping its execution). – Seth Carnegie Jan 11 '13 at 17:53
  • 1
    @kfmfe04: The promise IS the simpler thing. In particular `std::packaged_task` can be implemented from promise. You read a value from the shared state of future, and write it to that shared state using a promise. – Dave S Jan 11 '13 at 17:54
  • @DaveS I think he means that the simpler thing is returning a value from a thread instead of setting it by a `future`. – Seth Carnegie Jan 11 '13 at 17:55
  • Could you elaborate this "Also, a single thread can fulfill multiple futures with different values via promises, which it can't do otherwise." with an example? So I can't use a single thread to fulfill a bunch of packaged_tasks? That would be a good reason to use a promise over the others. – kfmfe04 Jan 11 '13 at 18:05
  • 1
    @kfmfe04 Even if you use a `packaged_task`, you can only return one value. With `promise`, you can, for example, capture 5 `promise`s and use them to communicate `5` values to the outside world. – Seth Carnegie Jan 11 '13 at 18:10
  • 2
    It is bit old thread, but I think there is one good example when `promise` is far better than `packaged_task`. Suppose your work thread do some task and have something to return, but after, has a lot of CLEAN UP tasks. I think it is better set a promise in this case, which may unlock the main thread, that is waiting this future, and meanwhile, do the cleaning with the work thread. – Vinícius A. Jorge Dec 27 '16 at 12:42
2

When you have two levels of async, you need to use a promise. Eg:

void fun()
{
std::promise<int> p;
std::future<int> f = p.get_future();
std::future<void> f2;
auto f3 = std::async([&]{
   // Do some other computation
   f2 = std::async([&]{ p.set_value(42);});
   // Do some other work
});
   // Do some other work
   // Now wait for the result of async work;
std::cout << f.get();
   // Do some other work again 
// Wait for async threads to complete
f3.wait();
f2.wait();
}
balki
  • 26,394
  • 30
  • 105
  • 151
  • @SethCarnegie Why would you use async that way, if all you do is wait for the value. Let me edit my example to make it clear. – balki Jan 11 '13 at 18:06
  • You can do other things in the outer thread, I was just giving an example why you don't need a `promise`. – Seth Carnegie Jan 11 '13 at 18:08
  • @SethCarnegie With edited code, is it possible to not use promise? – balki Jan 11 '13 at 18:15
  • It merely restates my answer now, because you are communicating two return values to `fun`. If you only communicated one and communicated the value after the thread is done with other work, then yes it would be possible. (As an aside, `f2` is redundant because you already wait for the inner thread by doing `f.get()`.) – Seth Carnegie Jan 11 '13 at 18:21
  • But it does emphasize more than mine how to communicate a value at another time than the end of the thread, so +1. – Seth Carnegie Jan 11 '13 at 18:39