Channels ▼
RSS

C/C++

Waiting for One-Off Events with Futures


Suppose you're going abroad by plane. Once you get to the airport and clear the various check-in procedures, you must wait — possibly for several hours — for notification that your flight is ready for boarding. Yes, you might be able to find some means of passing the time. You might read a book, surf the Internet, or eat at an overpriced airport cafe. But really you're just waiting for a one-time event: the signal that it's time to get on the plane.

The C++ Standard Library models this sort of one-off event with something called a future. If a thread needs to wait for a specific one-off event, then it obtains a future representing this event. The thread can then periodically wait on the future for short periods of time to see if the event has occurred (check the departures board) while performing some other task (eating in the overpriced cafe) between checks. Or it can do another task and then just wait for the future to become ready. A future may have data associated with it (such as your flight's boarding gate), or it may not. Once an event has happened and the future has become ready, then the future cannot be reset.

There are two sorts of futures in the C++ Standard Library, both implemented as class templates declared in the <future> library header: unique futures (std::future<>) and shared futures (std::shared_future<>). An instance of std::future is the one and only instance that refers to its associated event, whereas multiple instances of std::shared_future may refer to the same event. In the latter case, all the instances will become ready at the same time, and they may all access any data associated with the event. This associated data is the reason these are templates: As with std::unique_ptr and std::shared_ptr, the template parameter is the type of the associated data. The std:future<void> and std::shared_future<void> template specializations should be used where there's no associated data.

Although futures are used to communicate between threads, the future objects themselves do not provide synchronized access. If multiple threads need to access a single future object, then they must protect access via a mutex or another synchronization mechanism. However, as you'll see later in this article, multiple threads may each access their own copy of a std::shared_future<> without further synchronization, even if they all refer to the same asynchronous result.

The most basic of one-off events is the result of a calculation that has been run in the background. std::thread doesn't provide an easy means of returning a value from such a task, and this is addressed with futures — now you'll see how.

Returning Values from Background Tasks

Suppose you have a long-running calculation that will eventually yield a useful result, but you don't currently need the value. Maybe you've found a way to determine the answer to Life, the Universe, and Everything, to pinch an example from Douglas Adams. You could start a new thread to perform the calculation, but that means you would have to take care of transferring the result back, because std::thread doesn't provide a direct mechanism for doing so. This is where the std::async function template comes in.

You use std::async to start an asynchronous task for which you don't need the result right away. Rather than giving you a std::thread object to wait on, std::async returns a std::future object, which will eventually hold the return value of the function. When you need the value, you just call get() on the future, and the thread blocks until the future is ready and then returns the value. Here is a simple example:

#include <future>
#include <iostream>
int find_the_answer_to_ltuae();
void do_other_stuff();
int main()
{
  std::future<int> the_answer=std::async(find_the_answer_to_ltuae);
  do_other_stuff();
  std::cout<<"The answer is "<<the_answer.get()<<std::endl;
}

std::async allows you to pass additional arguments to the function by adding arguments to the call. If the first argument is a pointer to a member function, then the second argument provides the object on which to apply the function (directly, via a pointer, or wrapped in std::ref). The remaining arguments are passed as arguments to the member function. Otherwise, the second and subsequent arguments are passed as arguments to the function or callable object specified as the first argument. Just as with std::thread, if the arguments are rvalues, then the copies are created by moving the originals. This allows the use of move-only types as both the function object and the arguments, as illustrated here:

#include <string>
#include <future>
struct X
{
  void foo(int,std::string const&);
  std::string bar(std::string const&);
};
X x;
auto f1=std::async(&X::foo,&x,42,"hello"); 
//Calls p->foo(42,"hello") where p is &x
auto f2=std::async(&X::bar,x,"goodbye");  
//Calls tmpx.bar("goodbye") where tmpx is a copy of x
struct Y
{
  double operator()(double);
};
Y y;
auto f3=std::async(Y(),3.141);  
//Calls tmpy(3.141) where tmpy is move-constructed from Y()
auto f4=std::async(std::ref(y),2.718);  
//Calls y(2.718)
X baz(X&);
std::async(baz,std::ref(x));  
//Calls baz(x)
class move_only
{
public:
  move_only();
  move_only(move_only&&)
  move_only(move_only const&) = delete;
  move_only& operator=(move_only&&);
  move_only& operator=(move_only const&) = delete;
  void operator()();
};
auto f5=std::async(move_only());  
//Calls tmp() where tmp is constructed from std::move(move_only())

The use of std::async makes it easy to divide algorithms into tasks that can be run concurrently. However, it's not the only way to associate a std::future with a task. You can also do it by wrapping the task in an instance of the std::packaged_task<> class template or by writing code to set the values explicitly using the std::promise<> class template. std::packaged_task is a higher-level abstraction than std::promise, so we'll start with that.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video