Packaged task
The std::async
is not the only way to associate a
std::future
with a task. Using the
std::packaged_task
is also a possible solution.
Packaged task
We would like to represent the result of the function
with a future using a packaged task. This is done in three steps.
Creation
First, we create a packaged task:
The constructor of the std::packaged_task
requires a function
(callable target) as an argument. The template parameter is the signature of the
function. The signature is int(double, bool)
because
complicated_computation
returns int
and
takes arguments of types double
and bool
.
Future
The packaged task has a member function
.get_future()
which returns a future.
The template parameter of the std::future
must be the same as
the return type of the function which was the argument for the packaged task
constructor. In our example, the template parameter must be
int
because complicated_computation
(which
returns int
) was the argument for the task
constructor.
Execution
The packaged task is a callable object. The task
forwards the
arguments to the supplied function.
The upper call runs the complicated_computation
with
arguments (2.5, true)
and stores the result of the function
in the future.
We can pass the packaged task around and call it at a different place than where it was created. This is the benefit of packaged tasks: decoupling the creation of the future with the execution of the task.
Typical usage
The typical usage of a packaged task is:
-
creating the packaged task with a function,
-
retrieving the future from the packaged task,
-
passing the packaged task elsewhere,
-
invoking the packaged task.
Temperature tomorrow revisited
We will use a packaged task on the example from the previous article.
The story was: A couple, wife and husband, are going on a picnic tomorrow. The wife would like to know what will be the temperature of the weather. Therefore, she asks her husband to look it up.
The two functions from the last example
stay the same. The make_break()
stops current thread for
given amount of milliseconds and the temperature()
returns the
temperature, which the husband looked up. The main
function
is different
The interesting part is where the constructor of std::package_task
creates a packaged task with the
temperature()
. Then, the future answer
is
retrieved from the task
. Afterwards, we pass the task to a
separate thread. The packaged task is not copyable, therefore
std::move
moves it to the thread. The thread is detached and
computes the result asynchronously.
If the temperature
would have additional input arguments,
they would be listed in the std::thread
constructor.
The entire source code is available here. The output of the program is:
This is a possible usage of a packaged task but it is not a common one. Using
std::async
is a lot easier in this case. But it is useful to
start exploring packaged tasks with a familiar example.
Summary
The std::packaged_task
is another way to associate a task
with a std::future
– the future representation of the result
of the task.
In the next article, we will give another example of usage of packaged
tasks. It will take advantage of the fact that the execution of the task and the
retrieval of the future from the packaged task is not so tightly connected as in
std::async
.
Links: