<ahref="#WhatIsAnAsyncTask">What is an Async Task?</a>
</li>
<liclass="level1">
<ahref="#LaunchAsynchronousTasksFromAnExecutor">Launch Async Tasks from an Executor</a>
</li>
<liclass="level1">
<ahref="#LaunchAsynchronousTasksFromARuntime">Launch Async Tasks from a Runtime</a>
</li>
<liclass="level1">
<ahref="#LaunchAsynchronousTasksRecursivelyFromARuntime">Launch Async Tasks Recursively from a Runtime</a>
</li>
</ul>
</div>
<divclass="textblock"><p>Taskflow provides mechanisms to launch tasks asynchronously, enabling dynamic parallelism that goes beyond static task graphs.</p>
<h1><aclass="anchor" id="WhatIsAnAsyncTask"></a>
What is an Async Task?</h1>
<p>An async task is a callable object submitted for execution without being embedded in a pre-defined task graph. Unlike regular taskflow tasks whose dependencies are declared upfront, async tasks are created and dispatched on the fly, making them suitable for dynamic, recursive, or data-dependent parallelism that cannot be fully determined at graph construction time.</p>
<p>The C++ standard library provides <code>std::async</code> for this purpose. However, <code>std::async</code> has fundamental limitations that make it ill-suited for high-performance parallel programs:</p>
<divclass="fragment"><divclass="line"><spanclass="comment">// std::async typically spawns a new OS thread for each call</span></div>
<divclass="line">std::future<int> f1 = std::async(std::launch::async, []() { <spanclass="keywordflow">return</span> 1; });</div>
<divclass="line"><spanclass="comment">// ... spawning N tasks creates N threads, each with its own stack and</span></div>
<divclass="line"><spanclass="comment">// OS overhead — expensive to create, destroy, and context-switch</span></div>
</div><!-- fragment --><p>The three core problems with <code>std::async</code> are:</p>
<ul>
<li><b>No</b><b>thread</b><b>pool:</b> each call to <code>std::async</code> typically creates a brand-new OS thread, incurring significant creation and destruction overhead. Spawning hundreds of async tasks means hundreds of threads competing for CPU time.</li>
<li><b>No</b><b>scheduler:</b> there is no work-stealing or load balancing between <code>std::async</code> tasks. If one task finishes early, its thread sits idle rather than picking up work from overloaded threads.</li>
<li><b>No</b><b>task</b><b>graph</b><b>integration:</b><code>std::async</code> tasks are isolated from one another. You cannot express dependencies between them, embed them in a larger task graph, or coordinate them with other parallel work.</li>
</ul>
<p>Taskflow's async tasking addresses all three problems. Async tasks run on the executor's existing thread pool under the same work-stealing scheduler, integrate naturally with taskflows and runtimes, and can be launched from any thread without additional overhead.</p>
<p><aclass="el" href="classtf_1_1Executor.html#af960048056f7c6b5bc71f4f526f05df7" title="creates a parameterized asynchronous task to run the given function">tf::Executor::async</a> runs a callable asynchronously on the thread pool and returns a <code>std::future</code> that will eventually hold the result:</p>
</div><!-- fragment --><p>If you do not need the return value or do not require a <code>std::future</code> for synchronisation, use <aclass="el" href="classtf_1_1Executor.html#a0461cb2c459c9f9473c72af06af9c701" title="similar to tf::Executor::async but does not return a future object">tf::Executor::silent_async</a> instead. It returns nothing and incurs less overhead than <aclass="el" href="classtf_1_1Executor.html#af960048056f7c6b5bc71f4f526f05df7" title="creates a parameterized asynchronous task to run the given function">tf::Executor::async</a>, as it avoids the cost of managing a shared state:</p>
</div><!-- fragment --><p>Both <aclass="el" href="classtf_1_1Executor.html#af960048056f7c6b5bc71f4f526f05df7" title="creates a parameterized asynchronous task to run the given function">tf::Executor::async</a> and <aclass="el" href="classtf_1_1Executor.html#a0461cb2c459c9f9473c72af06af9c701" title="similar to tf::Executor::async but does not return a future object">tf::Executor::silent_async</a> are <em>thread-safe</em> and can be called from any thread — including worker threads already running inside the executor and external threads outside of it. The scheduler automatically detects the submission source and applies work-stealing to distribute the task efficiently across workers:</p>
<divclass="ttc" id="aclasstf_1_1Task_html"><divclass="ttname"><ahref="classtf_1_1Task.html">tf::Task</a></div><divclass="ttdoc">class to create a task handle over a taskflow node</div><divclass="ttdef"><b>Definition</b> task.hpp:569</div></div>
</div><!-- fragment --><dlclass="section note"><dt>Note</dt><dd>Async tasks created from an executor do not belong to any taskflow. Their lifetime is automatically managed by the executor.</dd></dl>
<p><aclass="el" href="classtf_1_1Runtime.html#a5688b13034f179c4a8b2b0ebbb215051" title="runs the given callable asynchronously">tf::Runtime::async</a> and <aclass="el" href="classtf_1_1Runtime.html#a0ce29efa2106c8c5a1432e4a55ab2e05" title="runs the given function asynchronously without returning any future object">tf::Runtime::silent_async</a> let you launch async tasks from within a running task that has access to a <aclass="el" href="classtf_1_1Runtime.html" title="class to create a runtime task">tf::Runtime</a> object. Like their executor counterparts, both methods are <em>thread-safe</em> and can be called from any context within the runtime's scope.</p>
<p>Unlike executor-level async tasks, tasks created from a runtime belong to that runtime and are implicitly joined at the end of its scope — meaning all async tasks spawned inside a runtime are guaranteed to finish before the runtime completes and control returns to the next task in the graph.</p>
<p>The example below spawns 100 async tasks from a runtime. Because of the implicit join, task <code>B</code> is guaranteed to see <code>counter</code> == 100:</p>
<divclass="ttc" id="aclasstf_1_1Executor_html"><divclass="ttname"><ahref="classtf_1_1Executor.html">tf::Executor</a></div><divclass="ttdoc">class to create an executor</div><divclass="ttdef"><b>Definition</b> executor.hpp:62</div></div>
<divclass="ttc" id="aclasstf_1_1Executor_html_a519777f5783981d534e9e53b99712069"><divclass="ttname"><ahref="classtf_1_1Executor.html#a519777f5783981d534e9e53b99712069">tf::Executor::run</a></div><divclass="ttdeci">tf::Future< void > run(Taskflow &taskflow)</div><divclass="ttdoc">runs a taskflow once</div></div>
<divclass="ttc" id="aclasstf_1_1FlowBuilder_html_a4d52a7fe2814b264846a2085e931652c"><divclass="ttname"><ahref="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">tf::FlowBuilder::emplace</a></div><divclass="ttdeci">Task emplace(C &&callable)</div><divclass="ttdoc">creates a static task</div><divclass="ttdef"><b>Definition</b> flow_builder.hpp:1781</div></div>
<divclass="ttc" id="aclasstf_1_1Runtime_html"><divclass="ttname"><ahref="classtf_1_1Runtime.html">tf::Runtime</a></div><divclass="ttdoc">class to create a runtime task</div><divclass="ttdef"><b>Definition</b> runtime.hpp:47</div></div>
<divclass="ttc" id="aclasstf_1_1Runtime_html_a0ce29efa2106c8c5a1432e4a55ab2e05"><divclass="ttname"><ahref="classtf_1_1Runtime.html#a0ce29efa2106c8c5a1432e4a55ab2e05">tf::Runtime::silent_async</a></div><divclass="ttdeci">void silent_async(F &&f)</div><divclass="ttdoc">runs the given function asynchronously without returning any future object</div><divclass="ttdef"><b>Definition</b> runtime.hpp:667</div></div>
<divclass="ttc" id="aclasstf_1_1Task_html_a8c78c453295a553c1c016e4062da8588"><divclass="ttname"><ahref="classtf_1_1Task.html#a8c78c453295a553c1c016e4062da8588">tf::Task::precede</a></div><divclass="ttdeci">Task & precede(Ts &&... tasks)</div><divclass="ttdoc">adds precedence links from this to other tasks</div><divclass="ttdef"><b>Definition</b> task.hpp:1305</div></div>
<divclass="ttc" id="aclasstf_1_1Taskflow_html"><divclass="ttname"><ahref="classtf_1_1Taskflow.html">tf::Taskflow</a></div><divclass="ttdoc">class to create a taskflow object</div><divclass="ttdef"><b>Definition</b> taskflow.hpp:64</div></div>
</div><!-- fragment --><p>Launching async tasks from a runtime is the key enabler for dynamic parallel algorithms — parallel reduction, divide-and-conquer, and recursive patterns — that need to create work at runtime rather than at graph construction time.</p>
Launch Async Tasks Recursively from a Runtime</h1>
<p>Async tasks spawned from a runtime can themselves accept a <aclass="el" href="classtf_1_1Runtime.html" title="class to create a runtime task">tf::Runtime</a> reference, allowing them to recursively spawn further async tasks. Combined with <aclass="el" href="classtf_1_1Runtime.html#aba54a7cacffb54f5eb133730d256a7c4" title="corun all tasks spawned by this runtime with other workers">tf::Runtime::corun</a>, this enables fork-join style divide-and-conquer parallelism where each level of recursion fans out work to available workers without blocking any thread.</p>
<p>The example below implements parallel Fibonacci using recursive async tasking:</p>
<divclass="line"> std::cout << N << <spanclass="stringliteral">"-th Fibonacci number is "</span> << res << <spanclass="charliteral">'\n'</span>;</div>
<divclass="ttc" id="aclasstf_1_1Executor_html_a0461cb2c459c9f9473c72af06af9c701"><divclass="ttname"><ahref="classtf_1_1Executor.html#a0461cb2c459c9f9473c72af06af9c701">tf::Executor::silent_async</a></div><divclass="ttdeci">void silent_async(P &&params, F &&func)</div><divclass="ttdoc">similar to tf::Executor::async but does not return a future object</div></div>
<divclass="ttc" id="aclasstf_1_1Executor_html_ab9aa252f70e9a40020a1e5a89d485b85"><divclass="ttname"><ahref="classtf_1_1Executor.html#ab9aa252f70e9a40020a1e5a89d485b85">tf::Executor::wait_for_all</a></div><divclass="ttdeci">void wait_for_all()</div><divclass="ttdoc">waits for all tasks to complete</div></div>
<divclass="ttc" id="aclasstf_1_1Runtime_html_aba54a7cacffb54f5eb133730d256a7c4"><divclass="ttname"><ahref="classtf_1_1Runtime.html#aba54a7cacffb54f5eb133730d256a7c4">tf::Runtime::corun</a></div><divclass="ttdeci">void corun()</div><divclass="ttdoc">corun all tasks spawned by this runtime with other workers</div><divclass="ttdef"><b>Definition</b> runtime.hpp:642</div></div>
</div><!-- fragment --><dlclass="section note"><dt>Note</dt><dd><code>rt.corun()</code> without arguments waits for all async tasks spawned within the current runtime scope to complete, without blocking the underlying worker thread from executing other work in the meantime. This is what allows the recursive pattern to scale efficiently — a blocked worker can participate in executing the spawned children rather than idling.</dd></dl>
<p>The figure below shows the execution diagram for <code>fibonacci(4)</code>. The suffix <em>_1</em> denotes the left child spawned by its parent runtime:</p>
<divclass="dotgraph">
<iframescrolling="no" frameborder="0" src="dot_fibonacci_4_tail_optimized.svg" width="587" height="374"><p><b>This browser is not able to show SVG: try Firefox, Chrome, Safari, or Opera instead.</b></p></iframe></div>
</div></div><!-- contents -->
</div><!-- PageDoc -->
</div><!-- doc-content -->
<!-- HTML footer for doxygen 1.13.1-->
<!-- start footer part -->
<divid="nav-path" class="navpath"><!-- id is needed for treeview function! -->