liveBook · Manning

Std::packaged_task

As explained in C++ Concurrency in Action, Second Edition

`std::packaged_task` is a C++ standard library class template that wraps a callable and stores its result in a shared state accessible through a future.

`std::packaged_task` wraps a function or other callable and connects it to a `std::future`. Calling the task invokes the callable and makes its result available through the future. The task does not choose where it runs: it can be called directly, passed to a `std::thread`, or handed to a task scheduler. section 4.2.2

The template argument is a function signature. In `std::packaged_task<T(Args...)>`, `get_future()` returns `std::future<T>`, and the task’s call operator accepts `Args...`. The wrapped callable’s parameter and return types need not match exactly if the required conversions are possible. section 4.2.2

Run a task on a thread

`std::packaged_task<int(int)> task([](int x) { return x*x; });` creates a task that accepts an `int` and produces an `int`. Get its future with `auto result = task.get_future();`, then run `std::thread worker(std::move(task), 7);`. After `worker.join();`, `result.get()` retrieves the returned value. Moving the task gives the thread the callable while the future remains with the caller. section 4.2.2 section 8.4.1

If the wrapped callable throws, the exception is stored instead of a result; `get()` rethrows it. Destroying an uninvoked task while its future is not ready instead makes the future report a `broken_promise` error. These guarantees cover the task’s result, not every exception-safety obligation around it: code that starts threads must still arrange to join them. section 4.2.4 section 8.4.1

Listing 4.9. Running code on a GUI thread using std::packaged_task
#include <deque>
#include <mutex>
#include <future>
#include <thread>
#include <utility>
std::mutex m;
std::deque<std::packaged_task<void()> > tasks;
bool gui_shutdown_message_received();
void get_and_process_gui_message();
void gui_thread()                               #1
{
    while(!gui_shutdown_message_received())     #2
    {
        get_and_process_gui_message();          #3
        std::packaged_task<void()> task;
        {
            std::lock_guard<std::mutex> lk(m);
            if(tasks.empty())                   #4
                continue;
            task=std::move(tasks.front());      #5
            tasks.pop_front();
        }
        task();                                 #6
    }
}
std::thread gui_bg_thread(gui_thread);
template<typename Func>
std::future<void> post_task_for_gui_thread(Func f)
{
    std::packaged_task<void()> task(f);         #7
    std::future<void> res=task.get_future();    #8
    std::lock_guard<std::mutex> lk(m);
    tasks.push_back(std::move(task));           #9
    return res;                                 #10
}


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":9,\"ch\":48},{\"line\":9,\"ch\":50}],[{\"line\":33,\"ch\":48},{\"line\":33,\"ch\":50}],[{\"line\":11,\"ch\":48},{\"line\":11,\"ch\":50}],[{\"line\":13,\"ch\":48},{\"line\":13,\"ch\":50}],[{\"line\":17,\"ch\":48},{\"line\":17,\"ch\":50}],[{\"line\":19,\"ch\":48},{\"line\":19,\"ch\":50}],[{\"line\":22,\"ch\":48},{\"line\":22,\"ch\":50}],[{\"line\":29,\"ch\":48},{\"line\":29,\"ch\":50}],[{\"line\":30,\"ch\":48},{\"line\":30,\"ch\":50}],[{\"line\":32,\"ch\":48},{\"line\":32,\"ch\":50}],[{\"line\":33,\"ch\":48},{\"line\":33,\"ch\":51}]]"}
!@%STYLE%@!

A GUI thread takes a `std::packaged_task<void()>` from a mutex-protected queue, releases the lock, and invokes the task. The queue handles scheduling; the packaged task handles the connection to its future. section 4.2.2

The same separation helps parallel algorithms: each accumulation block can return its partial result through a `std::future<T>`, which also carries any exception thrown in that block. `std::async` offers another way to associate a future with work; the Concurrency TS version, `std::experimental::packaged_task`, instead produces an experimental future that supports continuations. section 8.4.1 section 4.4.3

How C++ Concurrency in Action, Second Edition covers it

How deep it goes
Explains signature typing, invocation, and exception propagation through worked GUI and parallel-algorithm uses rather than an exhaustive API reference.
Examples
Uses a GUI-thread task queue and parallel std::accumulate blocks to show task execution and future-based results.
What it assumes
Assumes familiarity with C++ callables and template signatures while introducing futures as a way to receive results from background work.
What you can do after
Build it from scratch: wrap callable blocks, run them on threads, retrieve results or exceptions through futures, and join threads safely.
How it connects
Relates packaged tasks to std::future, std::thread, thread pools, std::async, and exception safety.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of C++ Concurrency in Action, Second Edition. The section links point to the parts of the book it draws on.