class
#include <taskflow/core/taskflow.hpp>
Taskflow class to create a taskflow object
A taskflow manages a task dependency graph where each task represents a callable object (e.g., lambda, std::
- static task : the callable constructible from
std::function<void()>
- subflow task : the callable constructible from
std::function<void(tf::Subflow&)>
- condition task : the callable constructible from
std::function<int()>
- multi-condition task: the callable constructible from
std::function<tf::SmallVector<int>()>
- module task : the task constructed from tf::
Taskflow:: composed_of std::function<void(tf::Runtime&)>
Each task is a basic computation unit and is run by one worker thread from an executor. The following example creates a simple taskflow graph of four static tasks, A
, B
, C
, and D
, where A
runs before B
and C
and D
runs after B
and C
.
tf::Executor executor; tf::Taskflow taskflow("simple"); tf::Task A = taskflow.emplace([](){ std::cout << "TaskA\n"; }); tf::Task B = taskflow.emplace([](){ std::cout << "TaskB\n"; }); tf::Task C = taskflow.emplace([](){ std::cout << "TaskC\n"; }); tf::Task D = taskflow.emplace([](){ std::cout << "TaskD\n"; }); A.precede(B, C); // A runs before B and C D.succeed(B, C); // D runs after B and C executor.run(taskflow).wait();
The taskflow object itself is NOT thread-safe. You should not modifying the graph while it is running, such as adding new tasks, adding new dependencies, and moving the taskflow to another. To minimize the overhead of task creation, our runtime leverages a global object pool to recycle tasks in a thread-safe manner.
Please refer to Cookbook to learn more about each task type and how to submit a taskflow to an executor.
Base classes
- class FlowBuilder
- class to build a task dependency graph
Constructors, destructors, conversion operators
Public functions
- auto operator=(Taskflow&& rhs) -> Taskflow&
- move assignment operator
-
void dump(std::
ostream& ostream) const - dumps the taskflow to a DOT format through a std::
ostream target -
auto dump() const -> std::
string - dumps the taskflow to a std::
string of DOT format - auto num_tasks() const -> size_t
- queries the number of tasks
- auto empty() const -> bool
- queries the emptiness of the taskflow
-
void name(const std::
string&) - assigns a name to the taskflow
-
auto name() const -> const std::
string& - queries the name of the taskflow
- void clear()
- clears the associated task dependency graph
-
template<typename V>void for_each_task(V&& visitor) const
- applies a visitor to each task in the taskflow
- void remove_dependency(Task from, Task to)
- removes dependencies that go from task
from
to taskto
- auto graph() -> Graph&
- returns a reference to the underlying graph object
Function documentation
tf:: Taskflow:: Taskflow(const std:: string& name)
constructs a taskflow with the given name
tf::Taskflow taskflow("My Taskflow"); std::cout << taskflow.name(); // "My Taskflow"
tf:: Taskflow:: Taskflow(Taskflow&& rhs)
constructs a taskflow from a moved taskflow
Constructing a taskflow taskflow1
from a moved taskflow taskflow2
will migrate the graph of taskflow2
to taskflow1
. After the move, taskflow2
will become empty.
tf::Taskflow taskflow1(std::move(taskflow2)); assert(taskflow2.empty());
Notice that taskflow2
should not be running in an executor during the move operation, or the behavior is undefined.
tf:: Taskflow:: ~Taskflow() defaulted
default destructor
When the destructor is called, all tasks and their associated data (e.g., captured data) will be destroyed. It is your responsibility to ensure all submitted execution of this taskflow have completed before destroying it. For instance, the following code results in undefined behavior since the executor may still be running the taskflow while it is destroyed after the block.
{ tf::Taskflow taskflow; executor.run(taskflow); }
To fix the problem, we must wait for the execution to complete before destroying the taskflow.
{ tf::Taskflow taskflow; executor.run(taskflow).wait(); }
Taskflow& tf:: Taskflow:: operator=(Taskflow&& rhs)
move assignment operator
Moving a taskflow taskflow2
to another taskflow taskflow1
will destroy the existing graph of taskflow1
and assign it the graph of taskflow2
. After the move, taskflow2
will become empty.
taskflow1 = std::move(taskflow2); assert(taskflow2.empty());
Notice that both taskflow1
and taskflow2
should not be running in an executor during the move operation, or the behavior is undefined.
void tf:: Taskflow:: dump(std:: ostream& ostream) const
dumps the taskflow to a DOT format through a std::
taskflow.dump(std::cout); // dump the graph to the standard output std::ofstream ofs("output.dot"); taskflow.dump(ofs); // dump the graph to the file output.dot
For dynamically spawned tasks, such as module tasks, subflow tasks, and GPU tasks, you need to run the taskflow first before you can dump the entire graph.
tf::Task parent = taskflow.emplace([](tf::Subflow sf){ sf.emplace([](){ std::cout << "child\n"; }); }); taskflow.dump(std::cout); // this dumps only the parent tasks executor.run(taskflow).wait(); taskflow.dump(std::cout); // this dumps both parent and child tasks
std:: string tf:: Taskflow:: dump() const
dumps the taskflow to a std::
This method is similar to tf::Taskflow::dump(std::ostream& ostream), but returning a string of the graph in DOT format.
bool tf:: Taskflow:: empty() const
queries the emptiness of the taskflow
An empty taskflow has no tasks. That is the return of tf::
void tf:: Taskflow:: name(const std:: string&)
assigns a name to the taskflow
taskflow.name("assign another name");
const std:: string& tf:: Taskflow:: name() const
queries the name of the taskflow
std::cout << "my name is: " << taskflow.name();
void tf:: Taskflow:: clear()
clears the associated task dependency graph
When you clear a taskflow, all tasks and their associated data (e.g., captured data in task callables) will be destroyed. The behavior of clearing a running taskflow is undefined.
template<typename V>
void tf:: Taskflow:: for_each_task(V&& visitor) const
applies a visitor to each task in the taskflow
A visitor is a callable that takes an argument of type tf::
taskflow.for_each_task([](tf::Task task){ std::cout << task.name() << '\n'; });
void tf:: Taskflow:: remove_dependency(Task from,
Task to)
removes dependencies that go from task from
to task to
Parameters | |
---|---|
from | from task (dependent) |
to | to task (successor) |
tf::Taskflow taskflow; auto a = taskflow.placeholder().name("a"); auto b = taskflow.placeholder().name("b"); auto c = taskflow.placeholder().name("c"); auto d = taskflow.placeholder().name("d"); a.precede(b, c, d); assert(a.num_successors() == 3); assert(b.num_dependents() == 1); assert(c.num_dependents() == 1); assert(d.num_dependents() == 1); taskflow.remove_dependency(a, b); assert(a.num_successors() == 2); assert(b.num_dependents() == 0);