class
Executorclass to create an executor for running a taskflow graph
Contents
An executor manages a set of worker threads to run one or multiple taskflows using an efficient work-stealing scheduling algorithm.
// Declare an executor and a taskflow tf::Executor executor; tf::Taskflow taskflow; // Add three tasks into the taskflow tf::Task A = taskflow.emplace([] () { std::cout << "This is TaskA\n"; }); tf::Task B = taskflow.emplace([] () { std::cout << "This is TaskB\n"; }); tf::Task C = taskflow.emplace([] () { std::cout << "This is TaskC\n"; }); // Build precedence between tasks A.precede(B, C); tf::Future<void> fu = executor.run(taskflow); fu.wait(); // block until the execution completes executor.run(taskflow, [](){ std::cout << "end of 1 run"; }).wait(); executor.run_n(taskflow, 4); executor.wait_for_all(); // block until all associated executions finish executor.run_n(taskflow, 4, [](){ std::cout << "end of 4 runs"; }).wait(); executor.run_until(taskflow, [cnt=0] () mutable { return ++cnt == 10; });
All the run
methods are thread-safe. You can submit multiple taskflows at the same time to an executor from different threads.
Constructors, destructors, conversion operators
-
Executor(size_t N = std::
thread:: hardware_concurrency()) explicit - constructs the executor with
N
worker threads - ~Executor()
- destructs the executor
Public functions
-
auto run(Taskflow& taskflow) -> tf::
Future<void> - runs a taskflow once
-
auto run(Taskflow&& taskflow) -> tf::
Future<void> - runs a moved taskflow once
-
template<typename C>auto run(Taskflow& taskflow, C&& callable) -> tf::
Future<void> - runs a taskflow once and invoke a callback upon completion
-
template<typename C>auto run(Taskflow&& taskflow, C&& callable) -> tf::
Future<void> - runs a moved taskflow once and invoke a callback upon completion
-
auto run_n(Taskflow& taskflow,
size_t N) -> tf::
Future<void> - runs a taskflow for
N
times -
auto run_n(Taskflow&& taskflow,
size_t N) -> tf::
Future<void> - runs a moved taskflow for
N
times -
template<typename C>auto run_n(Taskflow& taskflow, size_t N, C&& callable) -> tf::
Future<void> - runs a taskflow for
N
times and then invokes a callback -
template<typename C>auto run_n(Taskflow&& taskflow, size_t N, C&& callable) -> tf::
Future<void> - runs a moved taskflow for
N
times and then invokes a callback -
template<typename P>auto run_until(Taskflow& taskflow, P&& pred) -> tf::
Future<void> - runs a taskflow multiple times until the predicate becomes true
-
template<typename P>auto run_until(Taskflow&& taskflow, P&& pred) -> tf::
Future<void> - runs a moved taskflow and keeps running it until the predicate becomes true
-
template<typename P, typename C>auto run_until(Taskflow& taskflow, P&& pred, C&& callable) -> tf::
Future<void> - runs a taskflow multiple times until the predicate becomes true and then invokes the callback
-
template<typename P, typename C>auto run_until(Taskflow&& taskflow, P&& pred, C&& callable) -> tf::
Future<void> - runs a moved taskflow and keeps running it until the predicate becomes true and then invokes the callback
-
template<typename T>void run_and_wait(T& target)
- runs a target graph and waits until it completes using an internal worker of this executor
- void wait_for_all()
- wait for all tasks to complete
- auto num_workers() const -> size_t noexcept
- queries the number of worker threads
- auto num_topologies() const -> size_t
- queries the number of running topologies at the time of this call
- auto num_taskflows() const -> size_t
- queries the number of running taskflows with moved ownership
- auto this_worker_id() const -> int
- queries the id of the caller thread in this executor
-
template<typename F, typename... ArgsT>auto async(F&& f, ArgsT && ... args) -> auto
- runs a given function asynchronously
-
template<typename F, typename... ArgsT>auto named_async(const std::
string& name, F&& f, ArgsT && ... args) -> auto - runs a given function asynchronously and gives a name to this task
-
template<typename F, typename... ArgsT>void silent_async(F&& f, ArgsT && ... args)
- similar to tf::
Executor:: async but does not return a future object -
template<typename F, typename... ArgsT>void named_silent_async(const std::
string& name, F&& f, ArgsT && ... args) - similar to tf::
Executor:: named_async but does not return a future object -
template<typename Observer, typename... ArgsT>auto make_observer(ArgsT && ... args) -> std::
shared_ptr<Observer> - constructs an observer to inspect the activities of worker threads
-
template<typename Observer>void remove_observer(std::
shared_ptr<Observer> observer) - removes an observer from the executor
- auto num_observers() const -> size_t noexcept
- queries the number of observers
Function documentation
tf:: Executor:: Executor(size_t N = std:: thread:: hardware_concurrency()) explicit
constructs the executor with N
worker threads
The constructor spawns N
worker threads to run tasks in a work-stealing loop. The number of workers must be greater than zero or an exception will be thrown. By default, the number of worker threads is equal to the maximum hardware concurrency returned by std::
tf:: Executor:: ~Executor()
destructs the executor
The destructor calls Executor::
tf:: Future<void> tf:: Executor:: run(Taskflow& taskflow)
runs a taskflow once
Parameters | |
---|---|
taskflow | a tf:: |
Returns | a tf:: |
This member function executes the given taskflow once and returns a tf::
tf::Future<void> future = executor.run(taskflow); // do something else future.wait();
This member function is thread-safe.
tf:: Future<void> tf:: Executor:: run(Taskflow&& taskflow)
runs a moved taskflow once
Parameters | |
---|---|
taskflow | a moved tf:: |
Returns | a tf:: |
This member function executes a moved taskflow once and returns a tf::
tf::Future<void> future = executor.run(std::move(taskflow)); // do something else future.wait();
This member function is thread-safe.
template<typename C>
tf:: Future<void> tf:: Executor:: run(Taskflow& taskflow,
C&& callable)
runs a taskflow once and invoke a callback upon completion
Parameters | |
---|---|
taskflow | a tf:: |
callable | a callable object to be invoked after this run |
Returns | a tf:: |
This member function executes the given taskflow once and invokes the given callable when the execution completes. This member function returns a tf::
tf::Future<void> future = executor.run(taskflow, [](){ std::cout << "done"; }); // do something else future.wait();
This member function is thread-safe.
template<typename C>
tf:: Future<void> tf:: Executor:: run(Taskflow&& taskflow,
C&& callable)
runs a moved taskflow once and invoke a callback upon completion
Parameters | |
---|---|
taskflow | a moved tf:: |
callable | a callable object to be invoked after this run |
Returns | a tf:: |
This member function executes a moved taskflow once and invokes the given callable when the execution completes. This member function returns a tf::
tf::Future<void> future = executor.run( std::move(taskflow), [](){ std::cout << "done"; } ); // do something else future.wait();
This member function is thread-safe.
tf:: Future<void> tf:: Executor:: run_n(Taskflow& taskflow,
size_t N)
runs a taskflow for N
times
Parameters | |
---|---|
taskflow | a tf:: |
N | number of runs |
Returns | a tf:: |
This member function executes the given taskflow N
times and returns a tf::
tf::Future<void> future = executor.run_n(taskflow, 2); // run taskflow 2 times // do something else future.wait();
This member function is thread-safe.
tf:: Future<void> tf:: Executor:: run_n(Taskflow&& taskflow,
size_t N)
runs a moved taskflow for N
times
Parameters | |
---|---|
taskflow | a moved tf:: |
N | number of runs |
Returns | a tf:: |
This member function executes a moved taskflow N
times and returns a tf::
tf::Future<void> future = executor.run_n( std::move(taskflow), 2 // run the moved taskflow 2 times ); // do something else future.wait();
This member function is thread-safe.
template<typename C>
tf:: Future<void> tf:: Executor:: run_n(Taskflow& taskflow,
size_t N,
C&& callable)
runs a taskflow for N
times and then invokes a callback
Parameters | |
---|---|
taskflow | a tf:: |
N | number of runs |
callable | a callable object to be invoked after this run |
Returns | a tf:: |
This member function executes the given taskflow N
times and invokes the given callable when the execution completes. This member function returns a tf::
tf::Future<void> future = executor.run( taskflow, 2, [](){ std::cout << "done"; } // runs taskflow 2 times and invoke // the lambda to print "done" ); // do something else future.wait();
This member function is thread-safe.
template<typename C>
tf:: Future<void> tf:: Executor:: run_n(Taskflow&& taskflow,
size_t N,
C&& callable)
runs a moved taskflow for N
times and then invokes a callback
Parameters | |
---|---|
taskflow | a moved tf:: |
N | number of runs |
callable | a callable object to be invoked after this run |
Returns | a tf:: |
This member function executes a moved taskflow N
times and invokes the given callable when the execution completes. This member function returns a tf::
tf::Future<void> future = executor.run_n( // run the moved taskflow 2 times and invoke the lambda to print "done" std::move(taskflow), 2, [](){ std::cout << "done"; } ); // do something else future.wait();
This member function is thread-safe.
template<typename P>
tf:: Future<void> tf:: Executor:: run_until(Taskflow& taskflow,
P&& pred)
runs a taskflow multiple times until the predicate becomes true
Parameters | |
---|---|
taskflow | a tf:: |
pred | a boolean predicate to return true for stop |
Returns | a tf:: |
This member function executes the given taskflow multiple times until the predicate returns true
. This member function returns a tf::
tf::Future<void> future = executor.run_until( taskflow, [](){ return rand()%10 == 0 } ); // do something else future.wait();
This member function is thread-safe.
template<typename P>
tf:: Future<void> tf:: Executor:: run_until(Taskflow&& taskflow,
P&& pred)
runs a moved taskflow and keeps running it until the predicate becomes true
Parameters | |
---|---|
taskflow | a moved tf:: |
pred | a boolean predicate to return true for stop |
Returns | a tf:: |
This member function executes a moved taskflow multiple times until the predicate returns true
. This member function returns a tf::
tf::Future<void> future = executor.run_until( std::move(taskflow), [](){ return rand()%10 == 0 } ); // do something else future.wait();
This member function is thread-safe.
template<typename P, typename C>
tf:: Future<void> tf:: Executor:: run_until(Taskflow& taskflow,
P&& pred,
C&& callable)
runs a taskflow multiple times until the predicate becomes true and then invokes the callback
Parameters | |
---|---|
taskflow | a tf:: |
pred | a boolean predicate to return true for stop |
callable | a callable object to be invoked after this run completes |
Returns | a tf:: |
This member function executes the given taskflow multiple times until the predicate returns true
and then invokes the given callable when the execution completes. This member function returns a tf::
tf::Future<void> future = executor.run_until( taskflow, [](){ return rand()%10 == 0 }, [](){ std::cout << "done"; } ); // do something else future.wait();
This member function is thread-safe.
template<typename P, typename C>
tf:: Future<void> tf:: Executor:: run_until(Taskflow&& taskflow,
P&& pred,
C&& callable)
runs a moved taskflow and keeps running it until the predicate becomes true and then invokes the callback
Parameters | |
---|---|
taskflow | a moved tf:: |
pred | a boolean predicate to return true for stop |
callable | a callable object to be invoked after this run completes |
Returns | a tf:: |
This member function executes a moved taskflow multiple times until the predicate returns true
and then invokes the given callable when the execution completes. This member function returns a tf::
tf::Future<void> future = executor.run_until( std::move(taskflow), [](){ return rand()%10 == 0 }, [](){ std::cout << "done"; } ); // do something else future.wait();
This member function is thread-safe.
template<typename T>
void tf:: Executor:: run_and_wait(T& target)
runs a target graph and waits until it completes using an internal worker of this executor
Template parameters | |
---|---|
T | target type which has tf:: defined |
Parameters | |
target | the target task graph object |
The method runs a target graph which has tf::
defined and waits until the execution completes. Unlike the typical flow of calling tf::
series plus waiting on the result, this method must be called by an internal worker of this executor. The caller worker will participate in the work-stealing loop of the scheduler, therby avoiding potential deadlock caused by blocked waiting.
tf::Executor executor(2); tf::Taskflow taskflow; std::array<tf::Taskflow, 1000> others; std::atomic<size_t> counter{0}; for(size_t n=0; n<1000; n++) { for(size_t i=0; i<1000; i++) { others[n].emplace([&](){ counter++; }); } taskflow.emplace([&executor, &tf=others[n]](){ executor.run_and_wait(tf); //executor.run(tf).wait(); <- blocking the worker without doing anything // will introduce deadlock }); } executor.run(taskflow).wait();
The method is thread-safe as long as the target is not concurrently ran by two or more threads.
void tf:: Executor:: wait_for_all()
wait for all tasks to complete
This member function waits until all submitted tasks (e.g., taskflows, asynchronous tasks) to finish.
executor.run(taskflow1); executor.run_n(taskflow2, 10); executor.run_n(taskflow3, 100); executor.wait_for_all(); // wait until the above submitted taskflows finish
size_t tf:: Executor:: num_workers() const noexcept
queries the number of worker threads
Each worker represents one unique thread spawned by an executor upon its construction time.
tf::Executor executor(4); std::cout << executor.num_workers(); // 4
size_t tf:: Executor:: num_topologies() const
queries the number of running topologies at the time of this call
When a taskflow is submitted to an executor, a topology is created to store runtime metadata of the running taskflow. When the execution of the submitted taskflow finishes, its corresponding topology will be removed from the executor.
executor.run(taskflow); std::cout << executor.num_topologies(); // 0 or 1 (taskflow still running)
size_t tf:: Executor:: num_taskflows() const
queries the number of running taskflows with moved ownership
executor.run(std::move(taskflow)); std::cout << executor.num_taskflows(); // 0 or 1 (taskflow still running)
int tf:: Executor:: this_worker_id() const
queries the id of the caller thread in this executor
Each worker has an unique id in the range of 0
to N-1
associated with its parent executor. If the caller thread does not belong to the executor, -1
is returned.
tf::Executor executor(4); // 4 workers in the executor executor.this_worker_id(); // -1 (main thread is not a worker) taskflow.emplace([&](){ std::cout << executor.this_worker_id(); // 0, 1, 2, or 3 }); executor.run(taskflow);
template<typename F, typename... ArgsT>
auto tf:: Executor:: async(F&& f,
ArgsT && ... args)
runs a given function asynchronously
Template parameters | |
---|---|
F | callable type |
ArgsT | parameter types |
Parameters | |
f | callable object to call |
args | parameters to pass to the callable |
Returns | a tf:: |
The method creates an asynchronous task to launch the given function on the given arguments. Unlike std::async, the return here is a tf::std::nullopt
, or the value returned by the callable.
tf::Fugure<std::optional<int>> future = executor.async([](){ std::cout << "create an asynchronous task and returns 1\n"; return 1; });
This member function is thread-safe.
template<typename F, typename... ArgsT>
auto tf:: Executor:: named_async(const std:: string& name,
F&& f,
ArgsT && ... args)
runs a given function asynchronously and gives a name to this task
Template parameters | |
---|---|
F | callable type |
ArgsT | parameter types |
Parameters | |
name | name of the asynchronous task |
f | callable object to call |
args | parameters to pass to the callable |
Returns | a tf:: |
The method creates a named asynchronous task to launch the given function on the given arguments. Naming an asynchronous task is primarily used for profiling and visualizing the task execution timeline. Unlike std::async, the return here is a tf::std::nullopt
, or the value returned by the callable.
tf::Fugure<std::optional<int>> future = executor.named_async("name", [](){ std::cout << "create an asynchronous task with a name and returns 1\n"; return 1; });
This member function is thread-safe.
template<typename F, typename... ArgsT>
void tf:: Executor:: silent_async(F&& f,
ArgsT && ... args)
similar to tf::
This member function is more efficient than tf::
executor.silent_async([](){ std::cout << "create an asynchronous task with no return\n"; });
This member function is thread-safe.
template<typename F, typename... ArgsT>
void tf:: Executor:: named_silent_async(const std:: string& name,
F&& f,
ArgsT && ... args)
similar to tf::
This member function is more efficient than tf::
executor.named_silent_async("name", [](){ std::cout << "create an asynchronous task with a name and no return\n"; });
This member function is thread-safe.
template<typename Observer, typename... ArgsT>
std:: shared_ptr<Observer> tf:: Executor:: make_observer(ArgsT && ... args)
constructs an observer to inspect the activities of worker threads
Template parameters | |
---|---|
Observer | observer type derived from tf:: |
ArgsT | argument parameter pack |
Parameters | |
args | arguments to forward to the constructor of the observer |
Returns | a shared pointer to the created observer |
Each executor manages a list of observers with shared ownership with callers. For each of these observers, the two member functions, tf::
This member function is not thread-safe.
template<typename Observer>
void tf:: Executor:: remove_observer(std:: shared_ptr<Observer> observer)
removes an observer from the executor
This member function is not thread-safe.