A task group is a lightweight mechanism in Taskflow to spawn and manage a collection of asynchronous tasks cooperatively within a single executor. Task groups allow tasks to be executed recursively, asynchronously, or with dependencies, enabling efficient implementation of recursive parallel algorithms.
A task group (tf::TaskGroup) is created from a worker in an executor using tf::Executor::task_group(). Since task groups rely on cooperative execution, they must be created inside a task that is already running on the executor. For example, the code below creates a task group from an asynchronous task:
Internally, a task group is bound to the executor and the worker that creates it. This worker is referred to as the parent worker of the task group and is the only worker allowed to issue cooperative execution (tf::TaskGroup::corun) on that task group. Attempting to create a task group from a non-worker thread will result in an exception. This restriction ensures that task groups can safely participate in the executor's work-stealing loop and enables efficient cooperative execution while preserving the execution context required for recursion.
tf::TaskGroup supports submitting asynchronous tasks that execute cooperatively with other workers in the same executor. All tasks submitted to a task group are logically grouped and can be explicitly synchronized using tf::TaskGroup::corun(). The task group provides four categories of asynchronous submission APIs:
Each variant serves a distinct purpose depending on whether you need, including a returned future, dependency ordering between tasks, etc. For instance, the code below creates 100 tasks using tf::TaskGroup::silent_async and one task using tf::TaskGroup::async, followed by a tf::TaskGroup::corun() to cooperatively execute all tasks in the task group until every task has completed:
If you need dependencies among async tasks, use tf::TaskGroup::dependent_async or tf::TaskGroup::silent_dependent_async. For instance, the task group below builds a dynamic task graph of three tasks, A, B, and C, where C runs after A and B.
You can mark a task group as cancelled to stop any not-yet-started tasks in the group from running. Tasks that are already running will continue to completion, but no new tasks belonging to the task group will be scheduled after cancellation. The example below demonstrates how tf::TaskGroup::cancel() prevents pending tasks in a task group from executing , while allowing already running tasks to complete cooperatively. The first set of tasks deliberately occupies all but one worker thread, ensuring that subsequently spawned tasks remain pending. After invoking tf::TaskGroup::cancel(), these pending tasks are never scheduled, even after the blocked workers are released. A final call to tf::TaskGroup::corun() synchronizes with all tasks in the group, guaranteeing safe completion and verifying that cancellation successfully suppresses task execution.
Note that cancellation is cooperative: tasks should not assume immediate termination. Users must still call tf::TaskGroup::corun() to synchronize with all spawned tasks and ensure safe completion or cancellation. Failing to do so results in undefined behavior.
tf::TaskGroup is particularly well suited for implementing recursive task parallelism, where tasks dynamically spawn additional tasks during execution. Because task groups support cooperative execution via tf::TaskGroup::corun(), the worker thread can preserve its execution context across recursive calls. This design makes task groups a powerful choice for parallelizing recursive algorithms, such as divide-and-conquer, tree traversal, and dynamic programming. The example below demonstrates how to implement a parallel Fibonacci algorithm using a task group:
The function fibonacci spawns one recursive call as an asynchronous task and computes the other directly. Calling tf::TaskGroup::corun() ensures the asynchronous branch completes before the results are combined, while allowing the current worker to cooperatively execute spawned tasks and preserve its execution context.