tf::Executor taskflow/core/executor.hpp class friend class FlowBuilder FlowBuilder FlowBuilder class friend class Subflow Subflow Subflow class friend class Runtime Runtime Runtime const size_t const size_t tf::Executor::_MAX_STEALS _MAX_STEALS std::mutex std::mutex tf::Executor::_taskflows_mutex _taskflows_mutex std::vector< std::thread > std::vector<std::thread> tf::Executor::_threads _threads std::vector< Worker > std::vector<Worker> tf::Executor::_workers _workers DefaultNotifier DefaultNotifier tf::Executor::_notifier _notifier Latch Latch tf::Executor::_latch _latch std::condition_variable std::condition_variable tf::Executor::_topology_cv _topology_cv std::mutex std::mutex tf::Executor::_topology_mutex _topology_mutex size_t size_t tf::Executor::_num_topologies _num_topologies {0} std::atomic< bool > std::atomic<bool> tf::Executor::_done _done {0} std::list< Taskflow > std::list<Taskflow> tf::Executor::_taskflows _taskflows Freelist< Node * > Freelist<Node*> tf::Executor::_freelist _freelist std::unordered_set< std::shared_ptr< ObserverInterface > > std::unordered_set<std::shared_ptr<ObserverInterface> > tf::Executor::_observers _observers tf::Executor::Executor (size_t N=std::thread::hardware_concurrency()) Executor size_t N std::thread::hardware_concurrency() constructs the executor with N worker threads N the number of workers (default std::thread::hardware_concurrency) 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::thread::hardware_concurrency. tf::Executor::~Executor () ~Executor destructs the executor The destructor calls Executor::wait_for_all to wait for all submitted taskflows to complete and then notifies all worker threads to stop and join these threads. tf::Future< void > tf::Future<void> tf::Executor::run (Taskflow &taskflow) run Taskflow & taskflow runs a taskflow once taskflow a tf::Taskflow object a tf::Future that holds the result of the execution This member function executes the given taskflow once and returns a tf::Future object that eventually holds the result of the execution. tf::Future<void>future=executor.run(taskflow); //dosomethingelse future.wait(); This member function is thread-safe. The executor does not own the given taskflow. It is your responsibility to ensure the taskflow remains alive during its execution. tf::Future< void > tf::Future<void> tf::Executor::run (Taskflow &&taskflow) run Taskflow && taskflow runs a moved taskflow once taskflow a moved tf::Taskflow object a tf::Future that holds the result of the execution This member function executes a moved taskflow once and returns a tf::Future object that eventually holds the result of the execution. The executor will take care of the lifetime of the moved taskflow. tf::Future<void>future=executor.run(std::move(taskflow)); //dosomethingelse future.wait(); This member function is thread-safe. typename C tf::Future< void > tf::Future<void> tf::Executor::run (Taskflow &taskflow, C &&callable) run Taskflow & taskflow C && callable runs a taskflow once and invoke a callback upon completion taskflow a tf::Taskflow object callable a callable object to be invoked after this run a tf::Future that holds the result of the execution This member function executes the given taskflow once and invokes the given callable when the execution completes. This member function returns a tf::Future object that eventually holds the result of the execution. tf::Future<void>future=executor.run(taskflow,[](){std::cout<<"done";}); //dosomethingelse future.wait(); This member function is thread-safe. The executor does not own the given taskflow. It is your responsibility to ensure the taskflow remains alive during its execution. typename C tf::Future< void > tf::Future<void> tf::Executor::run (Taskflow &&taskflow, C &&callable) run Taskflow && taskflow C && callable runs a moved taskflow once and invoke a callback upon completion taskflow a moved tf::Taskflow object callable a callable object to be invoked after this run a tf::Future that holds the result of the execution This member function executes a moved taskflow once and invokes the given callable when the execution completes. This member function returns a tf::Future object that eventually holds the result of the execution. The executor will take care of the lifetime of the moved taskflow. tf::Future<void>future=executor.run( std::move(taskflow),[](){std::cout<<"done";} ); //dosomethingelse future.wait(); This member function is thread-safe. tf::Future< void > tf::Future<void> tf::Executor::run_n (Taskflow &taskflow, size_t N) run_n Taskflow & taskflow size_t N runs a taskflow for N times taskflow a tf::Taskflow object N number of runs a tf::Future that holds the result of the execution This member function executes the given taskflow N times and returns a tf::Future object that eventually holds the result of the execution. tf::Future<void>future=executor.run_n(taskflow,2);//runtaskflow2times //dosomethingelse future.wait(); This member function is thread-safe. The executor does not own the given taskflow. It is your responsibility to ensure the taskflow remains alive during its execution. tf::Future< void > tf::Future<void> tf::Executor::run_n (Taskflow &&taskflow, size_t N) run_n Taskflow && taskflow size_t N runs a moved taskflow for N times taskflow a moved tf::Taskflow object N number of runs a tf::Future that holds the result of the execution This member function executes a moved taskflow N times and returns a tf::Future object that eventually holds the result of the execution. The executor will take care of the lifetime of the moved taskflow. tf::Future<void>future=executor.run_n( std::move(taskflow),2//runthemovedtaskflow2times ); //dosomethingelse future.wait(); This member function is thread-safe. typename C tf::Future< void > tf::Future<void> tf::Executor::run_n (Taskflow &taskflow, size_t N, C &&callable) run_n Taskflow & taskflow size_t N C && callable runs a taskflow for N times and then invokes a callback taskflow a tf::Taskflow N number of runs callable a callable object to be invoked after this run a tf::Future that holds the result of the execution This member function executes the given taskflow N times and invokes the given callable when the execution completes. This member function returns a tf::Future object that eventually holds the result of the execution. tf::Future<void>future=executor.run( taskflow,2,[](){std::cout<<"done";}//runstaskflow2timesandinvoke //thelambdatoprint"done" ); //dosomethingelse future.wait(); This member function is thread-safe. The executor does not own the given taskflow. It is your responsibility to ensure the taskflow remains alive during its execution. typename C tf::Future< void > tf::Future<void> tf::Executor::run_n (Taskflow &&taskflow, size_t N, C &&callable) run_n Taskflow && taskflow size_t N C && callable runs a moved taskflow for N times and then invokes a callback taskflow a moved tf::Taskflow N number of runs callable a callable object to be invoked after this run a tf::Future that holds the result of the execution This member function executes a moved taskflow N times and invokes the given callable when the execution completes. This member function returns a tf::Future object that eventually holds the result of the execution. tf::Future<void>future=executor.run_n( //runthemovedtaskflow2timesandinvokethelambdatoprint"done" std::move(taskflow),2,[](){std::cout<<"done";} ); //dosomethingelse future.wait(); This member function is thread-safe. typename P tf::Future< void > tf::Future<void> tf::Executor::run_until (Taskflow &taskflow, P &&pred) run_until Taskflow & taskflow P && pred runs a taskflow multiple times until the predicate becomes true taskflow a tf::Taskflow pred a boolean predicate to return true for stop a tf::Future that holds the result of the execution This member function executes the given taskflow multiple times until the predicate returns true. This member function returns a tf::Future object that eventually holds the result of the execution. tf::Future<void>future=executor.run_until( taskflow,[](){returnrand()%10==0} ); //dosomethingelse future.wait(); This member function is thread-safe. The executor does not own the given taskflow. It is your responsibility to ensure the taskflow remains alive during its execution. typename P tf::Future< void > tf::Future<void> tf::Executor::run_until (Taskflow &&taskflow, P &&pred) run_until Taskflow && taskflow P && pred runs a moved taskflow and keeps running it until the predicate becomes true taskflow a moved tf::Taskflow object pred a boolean predicate to return true for stop a tf::Future that holds the result of the execution This member function executes a moved taskflow multiple times until the predicate returns true. This member function returns a tf::Future object that eventually holds the result of the execution. The executor will take care of the lifetime of the moved taskflow. tf::Future<void>future=executor.run_until( std::move(taskflow),[](){returnrand()%10==0} ); //dosomethingelse future.wait(); This member function is thread-safe. typename P typename C tf::Future< void > tf::Future<void> tf::Executor::run_until (Taskflow &taskflow, P &&pred, C &&callable) run_until Taskflow & taskflow P && pred C && callable runs a taskflow multiple times until the predicate becomes true and then invokes the callback taskflow a tf::Taskflow pred a boolean predicate to return true for stop callable a callable object to be invoked after this run completes a tf::Future that holds the result of the execution 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::Future object that eventually holds the result of the execution. tf::Future<void>future=executor.run_until( taskflow,[](){returnrand()%10==0},[](){std::cout<<"done";} ); //dosomethingelse future.wait(); This member function is thread-safe. The executor does not own the given taskflow. It is your responsibility to ensure the taskflow remains alive during its execution. typename P typename C tf::Future< void > tf::Future<void> tf::Executor::run_until (Taskflow &&taskflow, P &&pred, C &&callable) 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 taskflow a moved tf::Taskflow pred a boolean predicate to return true for stop callable a callable object to be invoked after this run completes a tf::Future that holds the result of the execution 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::Future object that eventually holds the result of the execution. The executor will take care of the lifetime of the moved taskflow. tf::Future<void>future=executor.run_until( std::move(taskflow), [](){returnrand()%10==0},[](){std::cout<<"done";} ); //dosomethingelse future.wait(); This member function is thread-safe. typename T void void tf::Executor::corun (T &target) corun T & target runs a target graph and waits until it completes using an internal worker of this executor T target type which has tf::Graph& T::graph() defined target the target task graph object The method runs a target graph which has tf::Graph& T::graph() defined and waits until the execution completes. Unlike the typical flow of calling tf::Executor::run 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::Executorexecutor(2); tf::Taskflowtaskflow; std::array<tf::Taskflow, 1000>others; std::atomic<size_t>counter{0}; for(size_tn=0;n<1000;n++){ for(size_ti=0;i<1000;i++){ others[n].emplace([&](){counter++;}); } taskflow.emplace([&executor,&tf=others[n]](){ executor.corun(tf); //executor.run(tf).wait();<-blockingtheworkerwithoutdoinganything //willintroducedeadlock }); } executor.run(taskflow).wait(); The method is thread-safe as long as the target is not concurrently ran by two or more threads. You must call tf::Executor::corun from a worker of the calling executor or an exception will be thrown. typename P void void tf::Executor::corun_until (P &&predicate) corun_until P && predicate keeps running the work-stealing loop until the predicate becomes true P predicate type 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([](){ returnfu.wait_for(std::chrono::seconds(0))==future_status::ready; }); }); You must call tf::Executor::corun_until from a worker of the calling executor or an exception will be thrown. void void tf::Executor::wait_for_all () 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();//waituntiltheabovesubmittedtaskflowsfinish size_t size_t tf::Executor::num_workers () const noexcept num_workers queries the number of worker threads Each worker represents one unique thread spawned by an executor upon its construction time. tf::Executorexecutor(4); std::cout<<executor.num_workers();//4 size_t size_t tf::Executor::num_topologies () const num_topologies 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();//0or1(taskflowstillrunning) size_t size_t tf::Executor::num_taskflows () const num_taskflows queries the number of running taskflows with moved ownership executor.run(std::move(taskflow)); std::cout<<executor.num_taskflows();//0or1(taskflowstillrunning) int int tf::Executor::this_worker_id () const this_worker_id 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::Executorexecutor(4);//4workersintheexecutor executor.this_worker_id();//-1(mainthreadisnotaworker) taskflow.emplace([&](){ std::cout<<executor.this_worker_id();//0,1,2,or3 }); executor.run(taskflow); typename Observer typename... ArgsT ArgsT std::shared_ptr< Observer > std::shared_ptr<Observer> tf::Executor::make_observer (ArgsT &&... args) make_observer ArgsT &&... args constructs an observer to inspect the activities of worker threads Observer observer type derived from tf::ObserverInterface ArgsT argument parameter pack args arguments to forward to the constructor of the observer 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::ObserverInterface::on_entry and tf::ObserverInterface::on_exit will be called before and after the execution of a task. This member function is not thread-safe. typename Observer void void tf::Executor::remove_observer (std::shared_ptr< Observer > observer) remove_observer std::shared_ptr< Observer > observer removes an observer from the executor This member function is not thread-safe. size_t size_t tf::Executor::num_observers () const noexcept num_observers queries the number of observers typename P typename F auto auto tf::Executor::async (P &&params, F &&func) async P && params F && func creates a parameterized asynchronous task to run the given function P task parameter type F callable type params task parameters func callable object a std::future that will hold the result of the execution The method creates a parameterized asynchronous task to run the given function and return a std::future object that eventually will hold the result of the execution. std::future<int>future=executor.async("name",[](){ std::cout<<"createanasynchronoustaskwithanameandreturns1\n"; return1; }); future.get(); This member function is thread-safe. typename F auto auto tf::Executor::async (F &&func) async F && func runs a given function asynchronously F callable type func callable object a std::future that will hold the result of the execution The method creates an asynchronous task to run the given function and return a std::future object that eventually will hold the result of the return value. std::future<int>future=executor.async([](){ std::cout<<"createanasynchronoustaskandreturns1\n"; return1; }); future.get(); This member function is thread-safe. typename P typename F void void tf::Executor::silent_async (P &&params, F &&func) silent_async P && params F && func similar to tf::Executor::async but does not return a future object F callable type params task parameters func callable object The method creates a parameterized asynchronous task to run the given function without returning any std::future object. This member function is more efficient than tf::Executor::async and is encouraged to use when applications do not need a std::future to acquire the result or synchronize the execution. executor.silent_async("name",[](){ std::cout<<"createanasynchronoustaskwithanameandnoreturn\n"; }); executor.wait_for_all(); This member function is thread-safe. typename F void void tf::Executor::silent_async (F &&func) silent_async F && func similar to tf::Executor::async but does not return a future object F callable type func callable object The method creates an asynchronous task to run the given function without returning any std::future object. This member function is more efficient than tf::Executor::async and is encouraged to use when applications do not need a std::future to acquire the result or synchronize the execution. executor.silent_async([](){ std::cout<<"createanasynchronoustaskwithnoreturn\n"; }); executor.wait_for_all(); This member function is thread-safe. typename F typename... Tasks Tasks std::enable_if_t< all_same_v< AsyncTask, std::decay_t< Tasks >... >, void > * nullptr tf::AsyncTask tf::AsyncTask tf::Executor::silent_dependent_async (F &&func, Tasks &&... tasks) silent_dependent_async F && func Tasks &&... tasks runs the given function asynchronously when the given dependents finish F callable type Tasks task types convertible to tf::AsyncTask func callable object tasks asynchronous tasks on which this execution depends a tf::AsyncTask handle This member function is more efficient than tf::Executor::dependent_async and is encouraged to use when you do not want a std::future to acquire the result or synchronize the execution. The example below creates three asynchronous tasks, A, B, and C, in which task C runs after task A and task B. tf::AsyncTaskA=executor.silent_dependent_async([](){printf("A\n");}); tf::AsyncTaskB=executor.silent_dependent_async([](){printf("B\n");}); executor.silent_dependent_async([](){printf("CrunsafterAandB\n");},A,B); executor.wait_for_all(); This member function is thread-safe. typename P typename F typename... Tasks Tasks std::enable_if_t< is_task_params_v< P > &&all_same_v< AsyncTask, std::decay_t< Tasks >... >, void > * nullptr tf::AsyncTask tf::AsyncTask tf::Executor::silent_dependent_async (P &&params, F &&func, Tasks &&... tasks) silent_dependent_async P && params F && func Tasks &&... tasks runs the given function asynchronously when the given dependents finish F callable type Tasks task types convertible to tf::AsyncTask params task parameters func callable object tasks asynchronous tasks on which this execution depends a tf::AsyncTask handle This member function is more efficient than tf::Executor::dependent_async and is encouraged to use when you do not want a std::future to acquire the result or synchronize the execution. The example below creates three asynchronous tasks, 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::AsyncTaskA=executor.silent_dependent_async("A",[](){printf("A\n");}); tf::AsyncTaskB=executor.silent_dependent_async("B",[](){printf("B\n");}); executor.silent_dependent_async( "C",[](){printf("CrunsafterAandB\n");},A,B ); executor.wait_for_all(); This member function is thread-safe. typename F typename I std::enable_if_t<!std::is_same_v< std::decay_t< I >, AsyncTask >, void > * nullptr tf::AsyncTask tf::AsyncTask tf::Executor::silent_dependent_async (F &&func, I first, I last) silent_dependent_async F && func I first I last runs the given function asynchronously when the given range of dependents finish F callable type I iterator type func callable object first iterator to the beginning (inclusive) last iterator to the end (exclusive) a tf::AsyncTask handle This member function is more efficient than tf::Executor::dependent_async and is encouraged to use when you do not want a std::future to acquire the result or synchronize the execution. The example below creates three asynchronous tasks, 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("CrunsafterAandB\n");},array.begin(),array.end() ); executor.wait_for_all(); This member function is thread-safe. 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::AsyncTask tf::Executor::silent_dependent_async (P &&params, F &&func, I first, I last) silent_dependent_async P && params F && func I first I last runs the given function asynchronously when the given range of dependents finish F callable type I iterator type params tasks parameters func callable object first iterator to the beginning (inclusive) last iterator to the end (exclusive) a tf::AsyncTask handle This member function is more efficient than tf::Executor::dependent_async and is encouraged to use when you do not want a std::future to acquire the result or synchronize the execution. The example below creates three asynchronous tasks, 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("CrunsafterAandB\n");},array.begin(),array.end() ); executor.wait_for_all(); This member function is thread-safe. typename F typename... Tasks Tasks std::enable_if_t< all_same_v< AsyncTask, std::decay_t< Tasks >... >, void > * nullptr auto auto tf::Executor::dependent_async (F &&func, Tasks &&... tasks) dependent_async F && func Tasks &&... tasks runs the given function asynchronously when the given dependents finish F callable type Tasks task types convertible to tf::AsyncTask func callable object tasks asynchronous tasks on which this execution depends a pair of a tf::AsyncTask handle and a std::future that holds the result of the execution 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::AsyncTask handle and a std::future<int> that eventually will hold the result of the execution. tf::AsyncTaskA=executor.silent_dependent_async([](){printf("A\n");}); tf::AsyncTaskB=executor.silent_dependent_async([](){printf("B\n");}); auto[C,fuC]=executor.dependent_async( [](){ printf("CrunsafterAandB\n"); return1; }, A,B ); fuC.get();//Cfinishes,whichinturnsmeansbothAandBfinish You can mixed the use of tf::AsyncTask handles returned by Executor::dependent_async and Executor::silent_dependent_async when specifying task dependencies. This member function is thread-safe. typename P typename F typename... Tasks Tasks std::enable_if_t< is_task_params_v< P > &&all_same_v< AsyncTask, std::decay_t< Tasks >... >, void > * nullptr auto auto tf::Executor::dependent_async (P &&params, F &&func, Tasks &&... tasks) dependent_async P && params F && func Tasks &&... tasks runs the given function asynchronously when the given dependents finish P task parameters type F callable type Tasks task types convertible to tf::AsyncTask params task parameters func callable object tasks asynchronous tasks on which this execution depends a pair of a tf::AsyncTask handle and a std::future that holds the result of the execution 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::AsyncTask handle and a std::future<int> that eventually will hold the result of the execution. Assigned task names will appear in the observers of the executor. tf::AsyncTaskA=executor.silent_dependent_async("A",[](){printf("A\n");}); tf::AsyncTaskB=executor.silent_dependent_async("B",[](){printf("B\n");}); auto[C,fuC]=executor.dependent_async( "C", [](){ printf("CrunsafterAandB\n"); return1; }, A,B ); assert(fuC.get()==1);//Cfinishes,whichinturnsmeansbothAandBfinish You can mixed the use of tf::AsyncTask handles returned by Executor::dependent_async and Executor::silent_dependent_async when specifying task dependencies. This member function is thread-safe. typename F typename I std::enable_if_t<!std::is_same_v< std::decay_t< I >, AsyncTask >, void > * nullptr auto auto tf::Executor::dependent_async (F &&func, I first, I last) dependent_async F && func I first I last runs the given function asynchronously when the given range of dependents finish F callable type I iterator type func callable object first iterator to the beginning (inclusive) last iterator to the end (exclusive) a pair of a tf::AsyncTask handle and a std::future that holds the result of the execution 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::AsyncTask handle and a std::future<int> that eventually will hold the result of the execution. 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("CrunsafterAandB\n"); return1; }, array.begin(),array.end() ); assert(fuC.get()==1);//Cfinishes,whichinturnsmeansbothAandBfinish You can mixed the use of tf::AsyncTask handles returned by Executor::dependent_async and Executor::silent_dependent_async when specifying task dependencies. This member function is thread-safe. 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 auto tf::Executor::dependent_async (P &&params, F &&func, I first, I last) dependent_async P && params F && func I first I last runs the given function asynchronously when the given range of dependents finish P task parameters type F callable type I iterator type params task parameters func callable object first iterator to the beginning (inclusive) last iterator to the end (exclusive) a pair of a tf::AsyncTask handle and a std::future that holds the result of the execution 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::AsyncTask handle and a std::future<int> that eventually will hold the result of the execution. 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");}) }; auto[C,fuC]=executor.dependent_async( "C", [](){ printf("CrunsafterAandB\n"); return1; }, array.begin(),array.end() ); assert(fuC.get()==1);//Cfinishes,whichinturnsmeansbothAandBfinish You can mixed the use of tf::AsyncTask handles returned by Executor::dependent_async and Executor::silent_dependent_async when specifying task dependencies. This member function is thread-safe. bool bool tf::Executor::_wait_for_task (Worker &, Node *&) _wait_for_task Worker & Node *& bool bool tf::Executor::_invoke_module_task_internal (Worker &, Node *) _invoke_module_task_internal Worker & Node * void void tf::Executor::_observer_prologue (Worker &, Node *) _observer_prologue Worker & Node * void void tf::Executor::_observer_epilogue (Worker &, Node *) _observer_epilogue Worker & Node * void void tf::Executor::_spawn (size_t) _spawn size_t void void tf::Executor::_exploit_task (Worker &, Node *&) _exploit_task Worker & Node *& void void tf::Executor::_explore_task (Worker &, Node *&) _explore_task Worker & Node *& void void tf::Executor::_schedule (Worker &, Node *) _schedule Worker & Node * void void tf::Executor::_schedule (Node *) _schedule Node * void void tf::Executor::_schedule (Worker &, const SmallVector< Node * > &) _schedule Worker & const SmallVector< Node * > & void void tf::Executor::_schedule (const SmallVector< Node * > &) _schedule const SmallVector< Node * > & void void tf::Executor::_set_up_topology (Worker *, Topology *) _set_up_topology Worker * Topology * void void tf::Executor::_set_up_graph (Graph &, Node *, Topology *, int, SmallVector< Node * > &) _set_up_graph Graph & Node * Topology * int SmallVector< Node * > & void void tf::Executor::_tear_down_topology (Worker &, Topology *) _tear_down_topology Worker & Topology * void void tf::Executor::_tear_down_async (Node *) _tear_down_async Node * void void tf::Executor::_tear_down_dependent_async (Worker &, Node *) _tear_down_dependent_async Worker & Node * void void tf::Executor::_tear_down_invoke (Worker &, Node *) _tear_down_invoke Worker & Node * void void tf::Executor::_increment_topology () _increment_topology void void tf::Executor::_decrement_topology () _decrement_topology void void tf::Executor::_invoke (Worker &, Node *) _invoke Worker & Node * void void tf::Executor::_invoke_static_task (Worker &, Node *) _invoke_static_task Worker & Node * void void tf::Executor::_invoke_subflow_task (Worker &, Node *) _invoke_subflow_task Worker & Node * void void tf::Executor::_detach_subflow_task (Worker &, Node *, Graph &) _detach_subflow_task Worker & Node * Graph & void void tf::Executor::_invoke_condition_task (Worker &, Node *, SmallVector< int > &) _invoke_condition_task Worker & Node * SmallVector< int > & void void tf::Executor::_invoke_multi_condition_task (Worker &, Node *, SmallVector< int > &) _invoke_multi_condition_task Worker & Node * SmallVector< int > & void void tf::Executor::_invoke_module_task (Worker &, Node *) _invoke_module_task Worker & Node * void void tf::Executor::_invoke_async_task (Worker &, Node *) _invoke_async_task Worker & Node * void void tf::Executor::_invoke_dependent_async_task (Worker &, Node *) _invoke_dependent_async_task Worker & Node * void void tf::Executor::_process_async_dependent (Node *, tf::AsyncTask &, size_t &) _process_async_dependent Node * tf::AsyncTask & size_t & void void tf::Executor::_process_exception (Worker &, Node *) _process_exception Worker & Node * void void tf::Executor::_schedule_async_task (Node *) _schedule_async_task Node * void void tf::Executor::_corun_graph (Worker &, Node *, Graph &) _corun_graph Worker & Node * Graph & typename P void void tf::Executor::_corun_until (Worker &, P &&) _corun_until Worker & P && 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. //Declareanexecutorandataskflow tf::Executorexecutor; tf::Taskflowtaskflow; //Addthreetasksintothetaskflow tf::TaskA=taskflow.emplace([](){std::cout<<"ThisisTaskA\n";}); tf::TaskB=taskflow.emplace([](){std::cout<<"ThisisTaskB\n";}); tf::TaskC=taskflow.emplace([](){std::cout<<"ThisisTaskC\n";}); //Buildprecedencebetweentasks A.precede(B,C); tf::Future<void>fu=executor.run(taskflow); fu.wait();//blockuntiltheexecutioncompletes executor.run(taskflow,[](){std::cout<<"endof1run";}).wait(); executor.run_n(taskflow,4); executor.wait_for_all();//blockuntilallassociatedexecutionsfinish executor.run_n(taskflow,4,[](){std::cout<<"endof4runs";}).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. tf::Executor_corun_graph tf::Executor_corun_until tf::Executor_decrement_topology tf::Executor_detach_subflow_task tf::Executor_done tf::Executor_exploit_task tf::Executor_explore_task tf::Executor_freelist tf::Executor_increment_topology tf::Executor_invoke tf::Executor_invoke_async_task tf::Executor_invoke_condition_task tf::Executor_invoke_dependent_async_task tf::Executor_invoke_module_task tf::Executor_invoke_module_task_internal tf::Executor_invoke_multi_condition_task tf::Executor_invoke_static_task tf::Executor_invoke_subflow_task tf::Executor_latch tf::Executor_MAX_STEALS tf::Executor_notifier tf::Executor_num_topologies tf::Executor_observer_epilogue tf::Executor_observer_prologue tf::Executor_observers tf::Executor_process_async_dependent tf::Executor_process_exception tf::Executor_schedule tf::Executor_schedule tf::Executor_schedule tf::Executor_schedule tf::Executor_schedule_async_task tf::Executor_set_up_graph tf::Executor_set_up_topology tf::Executor_spawn tf::Executor_taskflows tf::Executor_taskflows_mutex tf::Executor_tear_down_async tf::Executor_tear_down_dependent_async tf::Executor_tear_down_invoke tf::Executor_tear_down_topology tf::Executor_threads tf::Executor_topology_cv tf::Executor_topology_mutex tf::Executor_wait_for_task tf::Executor_workers tf::Executorasync tf::Executorasync tf::Executorcorun tf::Executorcorun_until tf::Executordependent_async tf::Executordependent_async tf::Executordependent_async tf::Executordependent_async tf::ExecutorExecutor tf::ExecutorFlowBuilder tf::Executormake_observer tf::Executornum_observers tf::Executornum_taskflows tf::Executornum_topologies tf::Executornum_workers tf::Executorremove_observer tf::Executorrun tf::Executorrun tf::Executorrun tf::Executorrun tf::Executorrun_n tf::Executorrun_n tf::Executorrun_n tf::Executorrun_n tf::Executorrun_until tf::Executorrun_until tf::Executorrun_until tf::Executorrun_until tf::ExecutorRuntime tf::Executorsilent_async tf::Executorsilent_async tf::Executorsilent_dependent_async tf::Executorsilent_dependent_async tf::Executorsilent_dependent_async tf::Executorsilent_dependent_async tf::ExecutorSubflow tf::Executorthis_worker_id tf::Executorwait_for_all tf::Executor~Executor