std::packaged_task is one of the possible ways of
associating a task with a
std::future. The benefit of the
packaged task is to decouple the creation of the future with the execution of
In the following, we will look how to exploit this decoupling.
Imagine a restaurant. There are a waiter and a cook. The procedure usually goes like this
- a customer orders a meal,
- the waiter records the order and passes it to the cook,
- the cook receives the order, cooks the meal and sends it back to the waiter,
- the waiter serves the meal.
In our particular example, we have a moody cook. He worked in a fast food restaurant before and he doesn’t want to make hamburgers any more. Unfortunately, our waiter is not aware of this issue.
We would like to model the restaurant with our code. The cook and the waiter
will be represented as two threads. The tasks
std::packaged_task) will be orders. The waiter will create
the task, hold its
std::future and push the task to the cook.
The cook will execute the task: make a meal out of the order.
Since we now know the general idea of the program, let’s look at the code.
The code begins with include statements. We declare global mutex, queue and condition variable. This variables will be used for passing the tasks between the threads. You should review the Passing data with condition variable if you are not comfortable passing data (in our case the package tasks) with condition variable.
The boolean variable
CLOSED marks whether the restaurant is
open or closed.
make_break function is familiar to us. It was a building
block of many previous examples. We use it to make the output nicer.
Next follows the code for the cook.
cooking() function loops until the restaurant closes. It
gets a cooking order from the queue with the help of the condition variable.
Passing data with condition
variable explains how this
works. Then, the function executes the cooking order – the cook prepares the
cook_the_meal(...) also models the cook. Here, the
moody behavior of the cook is defined. If the meal is hamburger, the cook
doesn’t want to prepare it, therefore the function returns
false. Otherwise, the cook prepares the meal and the
Now follows the code which describes the waiter.
Let’s explain it from the bottom up.
There are three orders: steak, hamburger and cheesecake. The waiter first adds
all three orders to the queue by calling the
function. The function returns the future which represents the boolean –
true if the meal is cooked or
something went wrong.
Later, the waiter serves the meals via
serve function. The
std::future is not copyable, therefore we need to use
std::move when passing the future as an argument.
The waiter checks the results of the cooking by calling the
.get() member function of the future. If it returns
true, everything went well and the waiter serves the meal.
Otherwise, the waiter apologies to the customer and kindly asks her/him to order
add_order function passes the orders between the threads
using the same technique as in Passing data with condition
It creates the package task from the string which represents the meal.
std::bind binds the function
cook_the_meal with the
argument. If we have
make_cake() is the same as
The advantage of using
std::bind is that we
pass only the
std::packaged_task< bool() > to the other
thread instead of passing
> together with all the arguments for the packaged tasks.
After the creation of packaged task, we get the future from the packaged task,
push the packaged task to the queue and notify the condition variable. (This
technique is the same as in Passing data with condition
variable.) Note that
std::packaged_task is not copyable, therefore
std::move moves it to the queue. At the end, the function
returns the future.
We decoupled the creation of the future with the execution of the task. The
future is created in
add_order. The task is executed
cooking, which will run in a different thread than
The main function creates two threads:
waiter with appropriate functions. After some time, we close
the restaurant and the main function ends.
The entire source code is available here.
Running the restaurant
This is one possible output of the program.
The picture below describes the flow of the program.
At the beginning, the waiter sends three orders to the cook. The cook picks up
the orders and starts making the meals. When the cook finishes the meal
(successfully or unsuccessfully), he informs the waiter about it (via
std::future). Once the waiter gets the prepared meal, he serves
The figure nicely describes the decoupling. The waiter’s thread is responsible for the creation of the packaged tasks and the cook’s thread is responsible for the execution of the tasks.
std::packaged_task decouples the creation of the future
with the execution of the task. We learned how to benefit from this
decoupling. We created tasks in one thread and we executed them in the other