class
#include <taskflow/core/executor.hpp>
Executor class to create an executor
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 corun(T& target)
- runs a target graph and waits until it completes using an internal worker of this executor
-
template<typename P>void corun_until(P&& predicate)
- keeps running the work-stealing loop until the predicate becomes true
- void wait_for_all()
- waits 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 within this executor
-
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
-
template<typename P, typename F>auto async(P&& params, F&& func) -> auto
- creates a parameterized asynchronous task to run the given function
-
template<typename F>auto async(F&& func) -> auto
- runs a given function asynchronously
-
template<typename P, typename F>void silent_async(P&& params, F&& func)
- similar to tf::
Executor:: async but does not return a future object -
template<typename F>void silent_async(F&& func)
- similar to tf::
Executor:: async but does not return a future object -
template<typename F, typename... Tasks, std::enable_if_t<all_same_v<AsyncTask, std::decay_t<Tasks>...>, void>* = nullptr>auto silent_dependent_async(F&& func, Tasks && ... tasks) -> tf::
AsyncTask - runs the given function asynchronously when the given dependents finish
-
template<typename P, typename F, typename... Tasks, std::enable_if_t<is_auto silent_dependent_async(P&& params, F&& func, Tasks && ... tasks) -> tf::
task_ params_ v<P> && all_same_v<AsyncTask, std::decay_t<Tasks>...>, void>* = nullptr> AsyncTask - runs the given function asynchronously when the given dependents finish
-
template<typename F, typename I, std::enable_if_t<!std::is_same_v<std::decay_t<I>, AsyncTask>, void>* = nullptr>auto silent_dependent_async(F&& func, I first, I last) -> tf::
AsyncTask - runs the given function asynchronously when the given range of dependents finish
-
template<typename P, typename F, typename I, std::enable_if_t<is_auto silent_dependent_async(P&& params, F&& func, I first, I last) -> tf::
task_ params_ v<P> && !std::is_same_v<std::decay_t<I>, AsyncTask>, void>* = nullptr> AsyncTask - runs the given function asynchronously when the given range of dependents finish
-
template<typename F, typename... Tasks, std::enable_if_t<all_same_v<AsyncTask, std::decay_t<Tasks>...>, void>* = nullptr>auto dependent_async(F&& func, Tasks && ... tasks) -> auto
- runs the given function asynchronously when the given dependents finish
-
template<typename P, typename F, typename... Tasks, std::enable_if_t<is_auto dependent_async(P&& params, F&& func, Tasks && ... tasks) -> auto
task_ params_ v<P> && all_same_v<AsyncTask, std::decay_t<Tasks>...>, void>* = nullptr> - runs the given function asynchronously when the given dependents finish
-
template<typename F, typename I, std::enable_if_t<!std::is_same_v<std::decay_t<I>, AsyncTask>, void>* = nullptr>auto dependent_async(F&& func, I first, I last) -> auto
- runs the given function asynchronously when the given range of dependents finish
-
template<typename P, typename F, typename I, std::enable_if_t<is_auto dependent_async(P&& params, F&& func, I first, I last) -> auto
task_ params_ v<P> && !std::is_same_v<std::decay_t<I>, AsyncTask>, void>* = nullptr> - runs the given function asynchronously when the given range of dependents finish
Function documentation
tf:: Executor:: Executor(size_t N = std:: thread:: hardware_concurrency()) explicit
constructs the executor with N
worker threads
Parameters | |
---|---|
N | the number of workers (default std:: |
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:: corun(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, thereby 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.corun(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.
template<typename P>
void tf:: Executor:: corun_until(P&& predicate)
keeps running the work-stealing loop until the predicate becomes true
Template parameters | |
---|---|
P | predicate type |
Parameters | |
predicate | a boolean predicate to indicate when to stop the loop |
The method keeps the caller worker running in the work-stealing loop until the stop predicate becomes true.
taskflow.emplace([&](){ std::future<void> fu = std::async([](){ std::sleep(100s); }); executor.corun_until([](){ return fu.wait_for(std::chrono::seconds(0)) == future_status::ready; }); });
void tf:: Executor:: wait_for_all()
waits 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 within 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 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.
template<typename P, typename F>
auto tf:: Executor:: async(P&& params,
F&& func)
creates a parameterized asynchronous task to run the given function
Template parameters | |
---|---|
P | task parameter type |
F | callable type |
Parameters | |
params | task parameters |
func | callable object |
Returns | a std:: |
The method creates a parameterized asynchronous task to run the given function and return a std::
std::future<int> future = executor.async("name", [](){ std::cout << "create an asynchronous task with a name and returns 1\n"; return 1; }); future.get();
This member function is thread-safe.
template<typename F>
auto tf:: Executor:: async(F&& func)
runs a given function asynchronously
Template parameters | |
---|---|
F | callable type |
Parameters | |
func | callable object |
Returns | a std:: |
The method creates an asynchronous task to run the given function and return a std::
std::future<int> future = executor.async([](){ std::cout << "create an asynchronous task and returns 1\n"; return 1; }); future.get();
This member function is thread-safe.
template<typename P, typename F>
void tf:: Executor:: silent_async(P&& params,
F&& func)
similar to tf::
Template parameters | |
---|---|
P | |
F | callable type |
Parameters | |
params | task parameters |
func | callable object |
The method creates a parameterized asynchronous task to run the given function without returning any std::
executor.silent_async("name", [](){ std::cout << "create an asynchronous task with a name and no return\n"; }); executor.wait_for_all();
This member function is thread-safe.
template<typename F>
void tf:: Executor:: silent_async(F&& func)
similar to tf::
Template parameters | |
---|---|
F | callable type |
Parameters | |
func | callable object |
The method creates an asynchronous task to run the given function without returning any std::
executor.silent_async([](){ std::cout << "create an asynchronous task with no return\n"; }); executor.wait_for_all();
This member function is thread-safe.
template<typename F, typename... Tasks, std::enable_if_t<all_same_v<AsyncTask, std::decay_t<Tasks>...>, void>* = nullptr>
tf:: AsyncTask tf:: Executor:: silent_dependent_async(F&& func,
Tasks && ... tasks)
runs the given function asynchronously when the given dependents finish
Template parameters | |
---|---|
F | callable type |
Tasks | task types convertible to tf:: |
Parameters | |
func | callable object |
tasks | asynchronous tasks on which this execution depends |
Returns | a tf:: |
This member function is more efficient than tf::A
, B
, and C
, in which task C
runs after task A
and task B
.
tf::AsyncTask A = executor.silent_dependent_async([](){ printf("A\n"); }); tf::AsyncTask B = executor.silent_dependent_async([](){ printf("B\n"); }); executor.silent_dependent_async([](){ printf("C runs after A and B\n"); }, A, B); executor.wait_for_all();
This member function is thread-safe.
template<typename P, typename F, typename... Tasks, std::enable_if_t<is_ task_ params_ v<P> && all_same_v<AsyncTask, std::decay_t<Tasks>...>, void>* = nullptr>
tf:: AsyncTask tf:: Executor:: silent_dependent_async(P&& params,
F&& func,
Tasks && ... tasks)
runs the given function asynchronously when the given dependents finish
Template parameters | |
---|---|
P | |
F | callable type |
Tasks | task types convertible to tf:: |
Parameters | |
params | task parameters |
func | callable object |
tasks | asynchronous tasks on which this execution depends |
Returns | a tf:: |
This member function is more efficient than tf::A
, B
, and C
, in which task C
runs after task A
and task B
. Assigned task names will appear in the observers of the executor.
tf::AsyncTask A = executor.silent_dependent_async("A", [](){ printf("A\n"); }); tf::AsyncTask B = executor.silent_dependent_async("B", [](){ printf("B\n"); }); executor.silent_dependent_async( "C", [](){ printf("C runs after A and B\n"); }, A, B ); executor.wait_for_all();
This member function is thread-safe.
template<typename F, typename I, std::enable_if_t<!std::is_same_v<std::decay_t<I>, AsyncTask>, void>* = nullptr>
tf:: AsyncTask tf:: Executor:: silent_dependent_async(F&& func,
I first,
I last)
runs the given function asynchronously when the given range of dependents finish
Template parameters | |
---|---|
F | callable type |
I | iterator type |
Parameters | |
func | callable object |
first | iterator to the beginning (inclusive) |
last | iterator to the end (exclusive) |
Returns | a tf:: |
This member function is more efficient than tf::A
, B
, and C
, in which task C
runs after task A
and task B
.
std::array<tf::AsyncTask, 2> array { executor.silent_dependent_async([](){ printf("A\n"); }), executor.silent_dependent_async([](){ printf("B\n"); }) }; executor.silent_dependent_async( [](){ printf("C runs after A and B\n"); }, array.begin(), array.end() ); executor.wait_for_all();
This member function is thread-safe.
template<typename P, typename F, typename I, std::enable_if_t<is_ task_ params_ v<P> && !std::is_same_v<std::decay_t<I>, AsyncTask>, void>* = nullptr>
tf:: AsyncTask tf:: Executor:: silent_dependent_async(P&& params,
F&& func,
I first,
I last)
runs the given function asynchronously when the given range of dependents finish
Template parameters | |
---|---|
P | |
F | callable type |
I | iterator type |
Parameters | |
params | tasks parameters |
func | callable object |
first | iterator to the beginning (inclusive) |
last | iterator to the end (exclusive) |
Returns | a tf:: |
This member function is more efficient than tf::A
, B
, and C
, in which task C
runs after task A
and task B
. Assigned task names will appear in the observers of the executor.
std::array<tf::AsyncTask, 2> array { executor.silent_dependent_async("A", [](){ printf("A\n"); }), executor.silent_dependent_async("B", [](){ printf("B\n"); }) }; executor.silent_dependent_async( "C", [](){ printf("C runs after A and B\n"); }, array.begin(), array.end() ); executor.wait_for_all();
This member function is thread-safe.
template<typename F, typename... Tasks, std::enable_if_t<all_same_v<AsyncTask, std::decay_t<Tasks>...>, void>* = nullptr>
auto tf:: Executor:: dependent_async(F&& func,
Tasks && ... tasks)
runs the given function asynchronously when the given dependents finish
Template parameters | |
---|---|
F | callable type |
Tasks | task types convertible to tf:: |
Parameters | |
func | callable object |
tasks | asynchronous tasks on which this execution depends |
Returns | a pair of a tf:: |
The example below creates three asynchronous tasks, A
, B
, and C
, in which task C
runs after task A
and task B
. Task C
returns a pair of its tf::
tf::AsyncTask A = executor.silent_dependent_async([](){ printf("A\n"); }); tf::AsyncTask B = executor.silent_dependent_async([](){ printf("B\n"); }); auto [C, fuC] = executor.dependent_async( [](){ printf("C runs after A and B\n"); return 1; }, A, B ); fuC.get(); // C finishes, which in turns means both A and B finish
You can mixed the use of tf::
This member function is thread-safe.
template<typename P, typename F, typename... Tasks, std::enable_if_t<is_ task_ params_ v<P> && all_same_v<AsyncTask, std::decay_t<Tasks>...>, void>* = nullptr>
auto tf:: Executor:: dependent_async(P&& params,
F&& func,
Tasks && ... tasks)
runs the given function asynchronously when the given dependents finish
Template parameters | |
---|---|
P | task parameters type |
F | callable type |
Tasks | task types convertible to tf:: |
Parameters | |
params | task parameters |
func | callable object |
tasks | asynchronous tasks on which this execution depends |
Returns | a pair of a tf:: |
The example below creates three named asynchronous tasks, A
, B
, and C
, in which task C
runs after task A
and task B
. Task C
returns a pair of its tf::
tf::AsyncTask A = executor.silent_dependent_async("A", [](){ printf("A\n"); }); tf::AsyncTask B = executor.silent_dependent_async("B", [](){ printf("B\n"); }); auto [C, fuC] = executor.dependent_async( "C", [](){ printf("C runs after A and B\n"); return 1; }, A, B ); assert(fuC.get()==1); // C finishes, which in turns means both A and B finish
You can mixed the use of tf::
This member function is thread-safe.
template<typename F, typename I, std::enable_if_t<!std::is_same_v<std::decay_t<I>, AsyncTask>, void>* = nullptr>
auto tf:: Executor:: dependent_async(F&& func,
I first,
I last)
runs the given function asynchronously when the given range of dependents finish
Template parameters | |
---|---|
F | callable type |
I | iterator type |
Parameters | |
func | callable object |
first | iterator to the beginning (inclusive) |
last | iterator to the end (exclusive) |
Returns | a pair of a tf:: |
The example below creates three asynchronous tasks, A
, B
, and C
, in which task C
runs after task A
and task B
. Task C
returns a pair of its tf::
std::array<tf::AsyncTask, 2> array { executor.silent_dependent_async([](){ printf("A\n"); }), executor.silent_dependent_async([](){ printf("B\n"); }) }; auto [C, fuC] = executor.dependent_async( [](){ printf("C runs after A and B\n"); return 1; }, array.begin(), array.end() ); assert(fuC.get()==1); // C finishes, which in turns means both A and B finish
You can mixed the use of tf::
This member function is thread-safe.
template<typename P, typename F, typename I, std::enable_if_t<is_ task_ params_ v<P> && !std::is_same_v<std::decay_t<I>, AsyncTask>, void>* = nullptr>
auto tf:: Executor:: dependent_async(P&& params,
F&& func,
I first,
I last)
runs the given function asynchronously when the given range of dependents finish
Template parameters | |
---|---|
P | task parameters type |
F | callable type |
I | iterator type |
Parameters | |
params | task parameters |
func | callable object |
first | iterator to the beginning (inclusive) |
last | iterator to the end (exclusive) |
Returns | a pair of a tf:: |
The example below creates three named asynchronous tasks, A
, B
, and C
, in which task C
runs after task A
and task B
. Task C
returns a pair of its tf::
std::array<tf::AsyncTask, 2> array { executor.silent_dependent_async("A", [](){ printf("A\n"); }), executor.silent_dependent_async("B", [](){ printf("B\n"); }) }; auto [C, fuC] = executor.dependent_async( "C", [](){ printf("C runs after A and B\n"); return 1; }, array.begin(), array.end() ); assert(fuC.get()==1); // C finishes, which in turns means both A and B finish
You can mixed the use of tf::
This member function is thread-safe.