class
#include <taskflow/core/task.hpp>
Task class to create a task handle over a taskflow node
A task points to a node in a taskflow graph and provides a set of methods for users to access and modify attributes of the associated node, such as dependencies, callable, names, and so on. A task is a very lightweight object (i.e., it only stores a node pointer) and can be trivially copied around.
// create two tasks with one dependency auto task1 = taskflow.emplace([](){}).name("task1"); auto task2 = taskflow.emplace([](){}).name("task2"); task1.precede(task2); // dump the task information through std::cout task1.dump(std::cout);
A task created from a taskflow can be one of the following types:
- tf::
TaskType:: STATIC - Static Tasking - tf::
TaskType:: CONDITION - Conditional Tasking - tf::
TaskType:: RUNTIME - Runtime Tasking - tf::
TaskType:: SUBFLOW - Subflow Tasking - tf::
TaskType:: MODULE - Composable Tasking
tf::Task task1 = taskflow.emplace([](){}).name("static task"); tf::Task task2 = taskflow.emplace([](){ return 3; }).name("condition task"); tf::Task task3 = taskflow.emplace([](tf::Runtime&){}).name("runtime task"); tf::Task task4 = taskflow.emplace([](tf::Subflow& sf){ tf::Task stask1 = sf.emplace([](){}); tf::Task stask2 = sf.emplace([](){}); }).name("subflow task"); tf::Task task5 = taskflow.composed_of(taskflow2).name("module task");
A tf::
tf::Task task = taskflow.emplace([](){}).name("static task"); task.work([](tf::Subflow& sf){ tf::Task stask1 = sf.emplace([](){}); tf::Task stask2 = sf.emplace([](){}); }).name("subflow task");
Constructors, destructors, conversion operators
Public functions
- auto operator=(const Task& other) -> Task&
- replaces the contents with a copy of the other task
-
auto operator=(std::
nullptr_t) -> Task& - replaces the contents with a null pointer
- auto operator==(const Task& rhs) const -> bool
- compares if two tasks are associated with the same taskflow node
- auto operator!=(const Task& rhs) const -> bool
- compares if two tasks are not associated with the same taskflow node
-
auto name() const -> const std::
string& - queries the name of the task
- auto num_successors() const -> size_t
- queries the number of successors of the task
- auto num_predecessors() const -> size_t
- queries the number of predecessors of the task
- auto num_strong_dependencies() const -> size_t
- queries the number of strong dependencies of the task
- auto num_weak_dependencies() const -> size_t
- queries the number of weak dependencies of the task
-
auto name(const std::
string& name) -> Task& - assigns a name to the task
-
template<typename C>auto work(C&& callable) -> Task&
- assigns a callable
-
template<typename T>auto composed_of(T& object) -> Task&
- creates a module task from a taskflow
-
template<typename... Ts>auto precede(Ts && ... tasks) -> Task&
- adds precedence links from this to other tasks
-
template<typename... Ts>auto succeed(Ts && ... tasks) -> Task&
- adds precedence links from other tasks to this
- auto release(Semaphore& semaphore) -> Task&
- makes the task release the given semaphore
-
template<typename I>auto release(I first, I last) -> Task&
- makes the task release the given range of semaphores
- auto acquire(Semaphore& semaphore) -> Task&
- makes the task acquire the given semaphore
-
template<typename I>auto acquire(I first, I last) -> Task&
- makes the task acquire the given range of semaphores
- auto data(void* data) -> Task&
- assigns pointer to user data
- void reset()
- resets the task handle to null
- void reset_work()
- resets the associated work to a placeholder
- auto empty() const -> bool
- queries if the task handle is associated with a taskflow node
- auto has_work() const -> bool
- queries if the task has a work assigned
-
template<typename V>void for_each_successor(V&& visitor) const
- applies an visitor callable to each successor of the task
-
template<typename V>void for_each_predecessor(V&& visitor) const
- applies an visitor callable to each predecessor of the task
-
template<typename V>void for_each_subflow_task(V&& visitor) const
- applies an visitor callable to each subflow task
- auto hash_value() const -> size_t
- obtains a hash value of the underlying node
- auto type() const -> TaskType
- returns the task type
-
void dump(std::
ostream& ostream) const - dumps the task through an output stream
- auto data() const -> void*
- queries pointer to user data
Function documentation
tf:: Task:: Task() defaulted
constructs an empty task
An empty task is not associated with any node in a taskflow.
Task& tf:: Task:: operator=(std:: nullptr_t)
replaces the contents with a null pointer
tf::Task A = taskflow.emplace([](){ std::cout << "A\n"; }); A = nullptr; // A no longer refers to any node
bool tf:: Task:: operator==(const Task& rhs) const
compares if two tasks are associated with the same taskflow node
Parameters | |
---|---|
rhs | the other task to compare with |
Returns | true if both tasks refer to the same node; false otherwise |
tf::Task A = taskflow.emplace([](){ std::cout << "A\n"; }); tf::Task B = A; assert(A == B); // A and B refer to the same node
bool tf:: Task:: operator!=(const Task& rhs) const
compares if two tasks are not associated with the same taskflow node
Parameters | |
---|---|
rhs | the other task to compare with |
Returns | true if they refer to different nodes; false otherwise |
tf::Task A = taskflow.emplace([](){ std::cout << "A\n"; }); tf::Task B = taskflow.emplace([](){ std::cout << "B\n"; }); assert(A != B); // A and B refer to different nodes
const std:: string& tf:: Task:: name() const
queries the name of the task
Returns | the name of the task as a constant string reference |
---|
tf::Task task = taskflow.emplace([](){}); task.name("MyTask"); std::cout << "Task name: " << task.name() << std::endl;
size_t tf:: Task:: num_successors() const
queries the number of successors of the task
Returns | the number of successor tasks. |
---|
tf::Task A = taskflow.emplace([](){}); tf::Task B = taskflow.emplace([](){}); A.precede(B); // B is a successor of A std::cout << "A has " << A.num_successors() << " successor(s)." << std::endl;
size_t tf:: Task:: num_predecessors() const
queries the number of predecessors of the task
Returns | the number of predecessor tasks |
---|
tf::Task A = taskflow.emplace([](){}); tf::Task B = taskflow.emplace([](){}); A.precede(B); // A is a predecessor of B std::cout << "B has " << B.num_predecessors() << " predecessor(s)." << std::endl;
size_t tf:: Task:: num_strong_dependencies() const
queries the number of strong dependencies of the task
Returns | the number of strong dependencies to this task |
---|
A strong dependency is a preceding link from one non-condition task to another task. For instance, task cond
below has one strong dependency, while tasks yes
and no
each have one weak dependency.
auto [init, cond, yes, no] = taskflow.emplace( [] () { }, [] () { return 0; }, [] () { std::cout << "yes\n"; }, [] () { std::cout << "no\n"; } ); cond.succeed(init) .precede(yes, no); // executes yes if cond returns 0 // executes no if cond returns 1
size_t tf:: Task:: num_weak_dependencies() const
queries the number of weak dependencies of the task
Returns | the number of weak dependencies to this task |
---|
A weak dependency is a preceding link from one condition task to another task. For instance, task cond
below has one strong dependency, while tasks yes
and no
each have one weak dependency.
auto [init, cond, yes, no] = taskflow.emplace( [] () { }, [] () { return 0; }, [] () { std::cout << "yes\n"; }, [] () { std::cout << "no\n"; } ); cond.succeed(init) .precede(yes, no); // executes yes if cond returns 0 // executes no if cond returns 1
Task& tf:: Task:: name(const std:: string& name)
assigns a name to the task
Parameters | |
---|---|
name | a std:: |
Returns | *this |
tf::Task task = taskflow.emplace([](){}).name("foo"); assert(task.name*) == "foo");
template<typename C>
Task& tf:: Task:: work(C&& callable)
assigns a callable
Template parameters | |
---|---|
C | callable type |
Parameters | |
callable | callable to construct a task |
Returns | *this |
A tf::
tf::Task task = taskflow.emplace([](){}).name("static task"); task.work([](tf::Subflow& sf){ tf::Task stask1 = sf.emplace([](){}); tf::Task stask2 = sf.emplace([](){}); }).name("subflow task");
template<typename T>
Task& tf:: Task:: composed_of(T& object)
creates a module task from a taskflow
Template parameters | |
---|---|
T | object type |
Parameters | |
object | a custom object that defines T::graph() method |
Returns | *this |
The example below creates a module task from a taskflow:
task.composed_of(taskflow);
To understand how Taskflow schedules a module task including how to create a schedulable graph, pleas refer to Create a Custom Composable Graph.
template<typename... Ts>
Task& tf:: Task:: precede(Ts && ... tasks)
adds precedence links from this to other tasks
Template parameters | |
---|---|
Ts | parameter pack |
Parameters | |
tasks | one or multiple tasks |
Returns | *this |
The example below creates a taskflow of two tasks, where task1
runs before task2
.
auto [task1, task2] = taskflow.emplace( [](){ std::cout << "task1\n"; }, [](){ std::cout << "task2\n"; } ); task1.precede(task2);
template<typename... Ts>
Task& tf:: Task:: succeed(Ts && ... tasks)
adds precedence links from other tasks to this
Template parameters | |
---|---|
Ts | parameter pack |
Parameters | |
tasks | one or multiple tasks |
Returns | *this |
The example below creates a taskflow of two tasks, where task1
runs before task2
.
auto [task1, task2] = taskflow.emplace( [](){ std::cout << "task1\n"; }, [](){ std::cout << "task2\n"; } ); task2.succeed(task1);
Task& tf:: Task:: data(void* data)
assigns pointer to user data
Parameters | |
---|---|
data | pointer to user data |
Returns | *this |
The following example shows how to attach a user data to a task and retrieve it during the execution of the task.
tf::Executor executor; tf::Taskflow taskflow("attach data to a task"); int data; // user data // create a task and attach it a user data auto A = taskflow.placeholder(); A.data(&data).work([A](){ auto d = *static_cast<int*>(A.data()); std::cout << "data is " << d << std::endl; }); // run the taskflow iteratively with changing data for(data = 0; data<10; data++){ executor.run(taskflow).wait(); }
void tf:: Task:: reset()
resets the task handle to null
Resetting a task will remove its associated taskflow node and make it an empty task.
tf::Task task = taskflow.emplace([](){}); assert(task.empty() == false); task.reset(); assert(task.empty() == true);
bool tf:: Task:: empty() const
queries if the task handle is associated with a taskflow node
Returns | true if the task is not associated with any taskflow node; otherwise false |
---|
tf::Task task; assert(task.empty() == true);
Note that an empty task is not equal to a placeholder task. A placeholder task is created from tf::
bool tf:: Task:: has_work() const
queries if the task has a work assigned
Returns | true if the task has a work assigned (not placeholder); otherwise false |
---|
tf::Task task = taskflow.placeholder(); assert(task.has_work() == false); // assign a static task callable to this task task.work([](){}); assert(task.has_work() == true);
template<typename V>
void tf:: Task:: for_each_successor(V&& visitor) const
applies an visitor callable to each successor of the task
Template parameters | |
---|---|
V | a callable type (function, lambda, etc.) that accepts a tf:: |
Parameters | |
visitor | visitor to apply to each subflow task |
This method allows you to traverse and inspect successor tasks of this task. For instance, the code below iterates the two successors (task2
and task3
) of task1
.
auto [task1, task2, task3] = taskflow.emplace( [](){ std::cout << "task 1\n"; }, [](){ std::cout << "task 2\n"; }, [](){ std::cout << "task 3\n"; } }); task1.precede(task2, task3); task1.for_each_successor([](tf::Task successor){ std::cout << "successor task " << successor.name() << '\n'; });
template<typename V>
void tf:: Task:: for_each_predecessor(V&& visitor) const
applies an visitor callable to each predecessor of the task
Template parameters | |
---|---|
V | a callable type (function, lambda, etc.) that accepts a tf:: |
Parameters | |
visitor | visitor to apply to each predecessor task |
This method allows you to traverse and inspect predecessor tasks of this task. For instance, the code below iterates the two predecessors (task2
and task3
) of task1
.
auto [task1, task2, task3] = taskflow.emplace( [](){ std::cout << "task 1\n"; }, [](){ std::cout << "task 2\n"; }, [](){ std::cout << "task 3\n"; } }); task1.succeed(task2, task3); task1.for_each_predecessor([](tf::Task predecessor){ std::cout << "predecessor task " << predecessor.name() << '\n'; });
template<typename V>
void tf:: Task:: for_each_subflow_task(V&& visitor) const
applies an visitor callable to each subflow task
Template parameters | |
---|---|
V | a callable type (function, lambda, etc.) that accepts a tf:: |
Parameters | |
visitor | visitor to apply to each subflow task |
This method allows you to traverse and inspect tasks within a subflow. It only applies to a subflow task.
tf::Task task = taskflow.emplace([](tf::Subflow& sf){ tf::Task stask1 = sf.emplace([](){}).name("stask1"); tf::Task stask2 = sf.emplace([](){}).name("stask2"); }); // Iterate tasks in the subflow and print each subflow task. task.for_each_subflow_task([](tf::Task stask){ std::cout << "subflow task " << stask.name() << '\n'; });
size_t tf:: Task:: hash_value() const
obtains a hash value of the underlying node
Returns | the hash value of the underlying node |
---|
The method returns std::
tf::Task task = taskflow.emplace([](){}); std::cout << "hash value of task is " << task.hash_value() << '\n';
TaskType tf:: Task:: type() const
returns the task type
A task can be one of the types defined in tf::
auto task = taskflow.emplace([](){}).name("task"); std::cout << task.name() << " type=[" << tf::to_string(task.type()) << "]\n";
void tf:: Task:: dump(std:: ostream& ostream) const
dumps the task through an output stream
The method dumps the name and the type of this task through std::
task.dump(std::cout);
void* tf:: Task:: data() const
queries pointer to user data
Returns | C-styled pointer to the attached user data by tf:: |
---|
The following example shows how to attach a user data to a task and retrieve it during the execution of the task.
tf::Executor executor; tf::Taskflow taskflow("attach data to a task"); int data; // user data // create a task and attach it a user data auto A = taskflow.placeholder(); A.data(&data).work([A](){ auto d = *static_cast<int*>(A.data()); std::cout << "data is " << d << std::endl; }); // run the taskflow iteratively with changing data for(data = 0; data<10; data++){ executor.run(taskflow).wait(); }