Futures and promises are tools to pass data from one thread to another. While this could also be done with other capabilities, such as global variables, futures and promises work without them. Furthermore, you don’t need to handle synchronization yourself.
A future is a variable which receives a value from another thread. If you access a future to get the value, you might need to wait until the other thread has provided the value. Boost.Thread provides boost::future
to define a future. The class defines a member function get()
to get the value. get()
is a blocking function that may have to wait for another thread.
To set a value in a future, you need to use a promise, because boost::future
does not provide a member function to set a value.
Boost.Thread provides the class boost::promise
, which has a member function set_value()
. You always use future and promise as a pair. You can get a future from a promise with get_future()
. You can use the future and the promise in different threads. If a value is set in the promise in one thread, it can be fetched from the future in another thread.
boost::future
and boost::promise
#define BOOST_THREAD_PROVIDES_FUTURE
#include <boost/thread.hpp>
#include <boost/thread/future.hpp>
#include <functional>
#include <iostream>
void accumulate(boost::promise<int> &p)
{
int sum = 0;
for (int i = 0; i < 5; ++i)
sum += i;
p.set_value(sum);
}
int main()
{
boost::promise<int> p;
boost::future<int> f = p.get_future();
boost::thread t{accumulate, std::ref(p)};
std::cout << f.get() << '\n';
}
Example 44.14 uses a future and a promise. The future f is received from the promise p. A reference to the promise is then passed to the thread t which executes the accumulate()
function. accumulate()
calculates the total of all numbers between 0 and 5 and saves it in the promise. In main()
get()
is called on the future to write the total to standard output.
The future f and the promise p are linked. When get()
is called on the future, the value stored in the promise with set_value()
is returned. Because the example uses two threads, it is possible that get()
will be called in main()
before accumulate()
has called set_value()
. In that case, get()
blocks until a value has been stored in the promise with set_value()
.
Example 44.14 displays 10
.
accumulate()
had to be adapted to be executed in a thread. It has to take a parameter of type boost::promise
and store the result in it. Example 44.15 introduces boost::packaged_task
, a class that forwards a value from any function to a future as long as the function returns the result with return
.
boost::packaged_task
#define BOOST_THREAD_PROVIDES_FUTURE
#include <boost/thread.hpp>
#include <boost/thread/future.hpp>
#include <utility>
#include <iostream>
int accumulate()
{
int sum = 0;
for (int i = 0; i < 5; ++i)
sum += i;
return sum;
}
int main()
{
boost::packaged_task<int> task{accumulate};
boost::future<int> f = task.get_future();
boost::thread t{std::move(task)};
std::cout << f.get() << '\n';
}
Example 44.15 is similar to the previous one, but this time boost::promise
is not used. Instead, this example uses the class boost::packaged_task
, which, like boost::promise
, provides a member function get_future()
that returns a future.
The constructor of boost::packaged_task
expects as a parameter the function that will be executed in a thread, but boost::packaged_task
doesn’t start a thread itself. An object of type boost::packaged_task
has to be passed to the constructor of boost::thread
for the function to be executed in a new thread.
The advantage of boost::packaged_task
is that it stores the return value of a function in a future. You don’t need to adapt a function to store its value in a future. boost::packaged_task
can be seen as an adapter which can store the return value of any function in a future.
While the example got rid of boost::promise
, the following example doesn’t use boost::packaged_task
and boost::thread
either.
boost::async()
#define BOOST_THREAD_PROVIDES_FUTURE
#include <boost/thread.hpp>
#include <boost/thread/future.hpp>
#include <iostream>
int accumulate()
{
int sum = 0;
for (int i = 0; i < 5; ++i)
sum += i;
return sum;
}
int main()
{
boost::future<int> f = boost::async(accumulate);
std::cout << f.get() << '\n';
}
In Example 44.16 accumulate()
is passed to the function boost::async()
. This function unifies boost::packaged_task
and boost::thread
. It starts accumulate()
in a new thread and returns a future.
It is possible to pass a launch policy to boost::async()
. This additional parameter determines whether boost::async()
will execute the function in a new thread or in the current thread. If you pass boost::launch::async, boost::async()
will start a new thread; this is the default behavior. If you pass boost::launch::deferred, the function will be executed in the current thread when get()
is called.
Although Boost 1.56.0 allows either boost::launch::async or boost::launch::deferred to be passed to boost::async()
, executing the function in the current thread is not yet implemented. If you pass boost::launch::deferred, the program will terminate.