<li><ahref="#AttachUserDataToATask">Attach User Data to a Task</a></li>
<li><ahref="#UnderstandTheLifetimeOfATask">Understand the Lifetime of a Task</a></li>
<li><ahref="#MoveATaskflow">Move a Taskflow</a></li>
</ul>
</nav>
<p>This chapter demonstrates how to create a static task dependency graph. Static tasking captures the static parallel structure of a decomposition and is defined only by the program itself. It has a flat task hierarchy and cannot spawn new tasks from a running dependency graph.</p><sectionid="CreateATaskDependencyGraph"><h2><ahref="#CreateATaskDependencyGraph">Create a Task Dependency Graph</a></h2><p>A task in Taskflow is a <em>callable</em> object for which the operation <ahref="https://en.cppreference.com/w/cpp/utility/functional/invoke">std::<wbr/>invoke</a> is applicable. It can be either a functor, a lambda expression, a bind expression, or a class objects with <code>operator()</code> overloaded. All tasks are created from <ahref="classtf_1_1Taskflow.html" class="m-doc">tf::<wbr/>Taskflow</a>, the class that manages a task dependency graph. Taskflow provides two methods, <ahref="classtf_1_1FlowBuilder.html#acab0b4ac82260f47fdb36a3244ee3aaf" class="m-doc">tf::<wbr/>Taskflow::<wbr/>placeholder</a> and <ahref="classtf_1_1FlowBuilder.html#a60d7a666cab71ecfa3010b2efb0d6b57" class="m-doc">tf::<wbr/>Taskflow::<wbr/>emplace</a> to create a task.</p><preclass="m-code"><spanclass="mi">1</span><spanclass="o">:</span><spanclass="w"></span><spanclass="n">tf</span><spanclass="o">::</span><spanclass="n">Taskflow</span><spanclass="w"></span><spanclass="n">taskflow</span><spanclass="p">;</span>
<spanclass="mi">9</span><spanclass="o">:</span><spanclass="w"></span><spanclass="p">);</span></pre><p>Debrief:</p><ul><li>Line 1 creates a taskflow object, or a <em>graph</em></li><li>Line 2 creates a placeholder task without work (i.e., callable)</li><li>Line 3 creates a task from a given callable object and returns a task handle</li><li>Lines 5-9 create three tasks in one call using C++ structured binding coupled with <ahref="http://en.cppreference.com/w/cpp/utility/tuple.html" class="m-doc-external">std::<wbr/>tuple</a></li></ul><p>Each time you create a task, the taskflow object creates a node in the task graph and returns a task handle of type <ahref="classtf_1_1Task.html" class="m-doc">tf::<wbr/>Task</a>. A task handle is a lightweight object that wraps up a particular node in a graph and provides a set of methods for you to assign different attributes to the task such as adding dependencies, naming, and assigning a new work.</p><preclass="m-code"><spanclass="w"></span><spanclass="mi">1</span><spanclass="o">:</span><spanclass="w"></span><spanclass="n">tf</span><spanclass="o">::</span><spanclass="n">Taskflow</span><spanclass="w"></span><spanclass="n">taskflow</span><spanclass="p">;</span>
<spanclass="w"></span><spanclass="mi">2</span><spanclass="o">:</span><spanclass="w"></span><spanclass="n">tf</span><spanclass="o">::</span><spanclass="n">Task</span><spanclass="w"></span><spanclass="n">A</span><spanclass="w"></span><spanclass="o">=</span><spanclass="w"></span><spanclass="n">taskflow</span><spanclass="p">.</span><spanclass="n">emplace</span><spanclass="p">([]</span><spanclass="w"></span><spanclass="p">()</span><spanclass="w"></span><spanclass="p">{</span><spanclass="w"></span><spanclass="n">std</span><spanclass="o">::</span><spanclass="n">cout</span><spanclass="w"></span><spanclass="o"><<</span><spanclass="w"></span><spanclass="s">"create a task A</span><spanclass="se">\n</span><spanclass="s">"</span><spanclass="p">;</span><spanclass="w"></span><spanclass="p">});</span>
<spanclass="w"></span><spanclass="mi">3</span><spanclass="o">:</span><spanclass="w"></span><spanclass="n">tf</span><spanclass="o">::</span><spanclass="n">Task</span><spanclass="w"></span><spanclass="n">B</span><spanclass="w"></span><spanclass="o">=</span><spanclass="w"></span><spanclass="n">taskflow</span><spanclass="p">.</span><spanclass="n">emplace</span><spanclass="p">([]</span><spanclass="w"></span><spanclass="p">()</span><spanclass="w"></span><spanclass="p">{</span><spanclass="w"></span><spanclass="n">std</span><spanclass="o">::</span><spanclass="n">cout</span><spanclass="w"></span><spanclass="o"><<</span><spanclass="w"></span><spanclass="s">"create a task B</span><spanclass="se">\n</span><spanclass="s">"</span><spanclass="p">;</span><spanclass="w"></span><spanclass="p">});</span>
<spanclass="w"></span><spanclass="mi">6</span><spanclass="o">:</span><spanclass="w"></span><spanclass="n">A</span><spanclass="p">.</span><spanclass="n">work</span><spanclass="p">([]</span><spanclass="w"></span><spanclass="p">()</span><spanclass="w"></span><spanclass="p">{</span><spanclass="w"></span><spanclass="n">std</span><spanclass="o">::</span><spanclass="n">cout</span><spanclass="w"></span><spanclass="o"><<</span><spanclass="w"></span><spanclass="s">"reassign A to a new callable</span><spanclass="se">\n</span><spanclass="s">"</span><spanclass="p">;</span><spanclass="w"></span><spanclass="p">});</span>
<spanclass="mi">14</span><spanclass="o">:</span><spanclass="w"></span><spanclass="n">std</span><spanclass="o">::</span><spanclass="n">cout</span><spanclass="w"></span><spanclass="o"><<</span><spanclass="w"></span><spanclass="n">B</span><spanclass="p">.</span><spanclass="n">num_dependents</span><spanclass="p">()</span><spanclass="w"></span><spanclass="o"><<</span><spanclass="w"></span><spanclass="n">std</span><spanclass="o">::</span><spanclass="n">endl</span><spanclass="p">;</span><spanclass="w"></span><spanclass="c1">// 1</span></pre><p>Debrief:</p><ul><li>Line 1 creates a taskflow object</li><li>Lines 2-3 create two tasks A and B</li><li>Lines 5-6 assign a name and a work to task A, and add a precedence link to task B</li><li>Line 7 adds a dependency link from A to B</li><li>Lines 9-14 dump the task attributes</li></ul><p>Taskflow uses general-purpose polymorphic function wrapper, <ahref="http://en.cppreference.com/w/cpp/utility/functional/function.html" class="m-doc-external">std::<wbr/>function</a>, to store and invoke a callable in a task. You need to follow its contract to create a task. For example, the callable to construct a task must be copyable, and thus the code below won't compile:</p><preclass="m-code"><spanclass="n">taskflow</span><spanclass="p">.</span><spanclass="n">emplace</span><spanclass="p">([</span><spanclass="n">ptr</span><spanclass="o">=</span><spanclass="n">std</span><spanclass="o">::</span><spanclass="n">make_unique</span><spanclass="o"><</span><spanclass="kt">int</span><spanclass="o">></span><spanclass="p">(</span><spanclass="mi">1</span><spanclass="p">)](){</span>
<spanclass="w"></span><spanclass="n">std</span><spanclass="o">::</span><spanclass="n">cout</span><spanclass="w"></span><spanclass="o"><<</span><spanclass="w"></span><spanclass="s">"captured unique pointer is not copyable"</span><spanclass="p">;</span>
<spanclass="p">});</span></pre></section><sectionid="VisualizeATaskDependencyGraph"><h2><ahref="#VisualizeATaskDependencyGraph">Visualize a Task Dependency Graph</a></h2><p>You can dump a taskflow to a DOT format and visualize the graph using free online tools such as <ahref="https://dreampuf.github.io/GraphvizOnline/">GraphvizOnline</a> and <ahref="http://www.webgraphviz.com/">WebGraphviz</a>.</p><preclass="m-code"><spanclass="w"></span><spanclass="mi">1</span><spanclass="o">:</span><spanclass="w"></span><spanclass="err">#</span><spanclass="n">include</span><spanclass="w"></span><spanclass="o"><</span><spanclass="n">taskflow</span><spanclass="o">/</span><spanclass="n">taskflow</span><spanclass="p">.</span><spanclass="n">hpp</span><spanclass="o">></span>
<spanclass="mi">20</span><spanclass="o">:</span><spanclass="w"></span><spanclass="p">}</span></pre><p>Debrief:</p><ul><li>Line 5 creates a taskflow object</li><li>Lines 8-11 create four tasks</li><li>Lines 14-17 add four task dependencies</li><li>Line 19 dumps the taskflow in the DOT format through standard output</li></ul><divclass="m-graph"><svgstyle="width: 24.200rem; height: 9.800rem;" viewBox="0.00 0.00 242.00 98.00">
</div></section><sectionid="ModifyTaskAttributes"><h2><ahref="#ModifyTaskAttributes">Modify Task Attributes</a></h2><p>This example demonstrates how to modify a task's attributes using methods defined in the task handler.</p><preclass="m-code"><spanclass="w"></span><spanclass="mi">1</span><spanclass="o">:</span><spanclass="w"></span><spanclass="err">#</span><spanclass="n">include</span><spanclass="w"></span><spanclass="o"><</span><spanclass="n">taskflow</span><spanclass="o">/</span><spanclass="n">taskflow</span><spanclass="p">.</span><spanclass="n">hpp</span><spanclass="o">></span>
<spanclass="w"></span><spanclass="mi">8</span><spanclass="o">:</span><spanclass="w"></span><spanclass="n">taskflow</span><spanclass="p">.</span><spanclass="n">placeholder</span><spanclass="p">(),</span><spanclass="w"></span><spanclass="c1">// create a task with no work</span>
<spanclass="w"></span><spanclass="mi">9</span><spanclass="o">:</span><spanclass="w"></span><spanclass="n">taskflow</span><spanclass="p">.</span><spanclass="n">placeholder</span><spanclass="p">()</span><spanclass="w"></span><spanclass="c1">// create a task with no work</span>
<spanclass="mi">12</span><spanclass="o">:</span><spanclass="w"></span><spanclass="n">tasks</span><spanclass="p">[</span><spanclass="mi">0</span><spanclass="p">].</span><spanclass="n">name</span><spanclass="p">(</span><spanclass="s">"This is Task 0"</span><spanclass="p">);</span>
<spanclass="mi">13</span><spanclass="o">:</span><spanclass="w"></span><spanclass="n">tasks</span><spanclass="p">[</span><spanclass="mi">1</span><spanclass="p">].</span><spanclass="n">name</span><spanclass="p">(</span><spanclass="s">"This is Task 1"</span><spanclass="p">);</span>
<spanclass="mi">16</span><spanclass="o">:</span><spanclass="w"></span><spanclass="k">for</span><spanclass="p">(</span><spanclass="k">auto</span><spanclass="w"></span><spanclass="n">task</span><spanclass="w"></span><spanclass="o">:</span><spanclass="w"></span><spanclass="n">tasks</span><spanclass="p">)</span><spanclass="w"></span><spanclass="p">{</span><spanclass="w"></span><spanclass="c1">// print out each task's attributes</span>
<spanclass="mi">22</span><spanclass="o">:</span><spanclass="w"></span><spanclass="n">taskflow</span><spanclass="p">.</span><spanclass="n">dump</span><spanclass="p">(</span><spanclass="n">std</span><spanclass="o">::</span><spanclass="n">cout</span><spanclass="p">);</span><spanclass="w"></span><spanclass="c1">// dump the taskflow graph</span>
<spanclass="mi">23</span><spanclass="o">:</span>
<spanclass="mi">24</span><spanclass="o">:</span><spanclass="w"></span><spanclass="n">tasks</span><spanclass="p">[</span><spanclass="mi">0</span><spanclass="p">].</span><spanclass="n">work</span><spanclass="p">([](){</span><spanclass="w"></span><spanclass="n">std</span><spanclass="o">::</span><spanclass="n">cout</span><spanclass="w"></span><spanclass="o"><<</span><spanclass="w"></span><spanclass="s">"got a new work!</span><spanclass="se">\n</span><spanclass="s">"</span><spanclass="p">;</span><spanclass="w"></span><spanclass="p">});</span>
<spanclass="mi">25</span><spanclass="o">:</span><spanclass="w"></span><spanclass="n">tasks</span><spanclass="p">[</span><spanclass="mi">1</span><spanclass="p">].</span><spanclass="n">work</span><spanclass="p">([](){</span><spanclass="w"></span><spanclass="n">std</span><spanclass="o">::</span><spanclass="n">cout</span><spanclass="w"></span><spanclass="o"><<</span><spanclass="w"></span><spanclass="s">"got a new work!</span><spanclass="se">\n</span><spanclass="s">"</span><spanclass="p">;</span><spanclass="w"></span><spanclass="p">});</span>
<spanclass="mi">28</span><spanclass="o">:</span><spanclass="w"></span><spanclass="p">}</span></pre><p>The output of this program looks like the following:</p><preclass="m-code">This is Task <spanclass="m">0</span>: <spanclass="nv">num_dependents</span><spanclass="o">=</span><spanclass="m">0</span>, <spanclass="nv">num_successors</span><spanclass="o">=</span><spanclass="m">1</span>
This is Task <spanclass="m">1</span>: <spanclass="nv">num_dependents</span><spanclass="o">=</span><spanclass="m">1</span>, <spanclass="nv">num_successors</span><spanclass="o">=</span><spanclass="m">0</span>
digraph Taskflow <spanclass="o">{</span>
<spanclass="s2">"This is Task 1"</span><spanclass="p">;</span>
<spanclass="s2">"This is Task 0"</span><spanclass="p">;</span>
<spanclass="s2">"This is Task 0"</span> -> <spanclass="s2">"This is Task 1"</span><spanclass="p">;</span>
<spanclass="o">}</span></pre><p>Debrief:</p><ul><li>Line 5 creates a taskflow object</li><li>Lines 7-10 create two placeholder tasks with no works and stores the corresponding task handles in a vector</li><li>Lines 12-13 name the two tasks with human-readable strings</li><li>Line 14 adds a dependency link from the first task to the second task</li><li>Lines 16-20 print out the name of each task, the number of dependents, and the number of successors</li><li>Line 22 dumps the task dependency graph to a <ahref="https://dreampuf.github.io/GraphvizOnline/">GraphViz Online</a> format (dot)</li><li>Lines 24-25 assign a new target to each task</li></ul><p>You can change the name and work of a task at anytime before running the graph. The later assignment overwrites the previous values.</p></section><sectionid="TraverseAdjacentTasks"><h2><ahref="#TraverseAdjacentTasks">Traverse Adjacent Tasks</a></h2><p>You can iterate the successor list and the dependent list of a task by using <ahref="classtf_1_1Task.html#aff13a503d4a3c994eb08cb6f22e1b427" class="m-doc">tf::<wbr/>Task::<wbr/>for_each_successor</a> and <ahref="classtf_1_1Task.html#a3bf68937662bf291637e4a763476b2e4" class="m-doc">tf::<wbr/>Task::<wbr/>for_each_dependent</a>, respectively. Each method takes a lambda and applies it to a successor or a dependent being traversed.</p><preclass="m-code"><spanclass="c1">// traverse all successors of my_task</span>
<spanclass="p">});</span></pre></section><sectionid="AttachUserDataToATask"><h2><ahref="#AttachUserDataToATask">Attach User Data to a Task</a></h2><p>You can attach custom data to a task using <ahref="classtf_1_1Task.html#afd82ab6d6518d1142a72c4d2c97ff114" class="m-doc">tf::<wbr/>Task::<wbr/>data(void*)</a> and access it using <ahref="classtf_1_1Task.html#afd82ab6d6518d1142a72c4d2c97ff114" class="m-doc">tf::<wbr/>Task::<wbr/>data()</a>. Each node in a taskflow is associated with a C-styled data pointer (i.e., <code>void*</code>) you can use to point to user data and access it in the body of a task callable. The following example attaches an integer to a task and accesses that integer through capturing the data in the callable.</p><preclass="m-code"><spanclass="kt">int</span><spanclass="w"></span><spanclass="n">my_data</span><spanclass="w"></span><spanclass="o">=</span><spanclass="w"></span><spanclass="mi">5</span><spanclass="p">;</span>
<spanclass="w"></span><spanclass="p">});</span></pre><p>Notice that you need to create a placeholder task first before assigning it a work callable. Only this way can you capture that task in the lambda and access its attached data in the lambda body.</p><asideclass="m-note m-warning"><h4>Attention</h4><p>It is your responsibility to ensure that the attached data stay alive during the execution of its task.</p></aside></section><sectionid="UnderstandTheLifetimeOfATask"><h2><ahref="#UnderstandTheLifetimeOfATask">Understand the Lifetime of a Task</a></h2><p>A task lives with its graph and belongs to only a graph at a time, and is not destroyed until the graph gets cleaned up. The lifetime of a task refers to the user-given callable object, including captured values. As long as the graph is alive, all the associated tasks exist.</p><asideclass="m-note m-warning"><h4>Attention</h4><p>It is your responsibility to keep tasks and graph alive during their execution.</p></aside></section><sectionid="MoveATaskflow"><h2><ahref="#MoveATaskflow">Move a Taskflow</a></h2><p>You can construct or assign a taskflow from a <em>moved</em> taskflow. Moving a taskflow to another will result in transferring the underlying graph data structures from one to the other.</p><preclass="m-code"><spanclass="n">tf</span><spanclass="o">::</span><spanclass="n">Taskflow</span><spanclass="w"></span><spanclass="n">taskflow1</span><spanclass="p">,</span><spanclass="w"></span><spanclass="n">taskflow3</span><spanclass="p">;</span>
<spanclass="n">assert</span><spanclass="p">(</span><spanclass="n">taskflow3</span><spanclass="p">.</span><spanclass="n">num_tasks</span><spanclass="p">()</span><spanclass="w"></span><spanclass="o">==</span><spanclass="w"></span><spanclass="mi">1</span><spanclass="w"></span><spanclass="o">&&</span><spanclass="w"></span><spanclass="n">taskflow2</span><spanclass="p">.</span><spanclass="n">num_tasks</span><spanclass="p">()</span><spanclass="w"></span><spanclass="o">==</span><spanclass="w"></span><spanclass="mi">0</span><spanclass="p">);</span></pre><p>You can only move a taskflow to another while that taskflow is not being run by an executor. Moving a running taskflow can result in undefined behavior. Please see <ahref="ExecuteTaskflow.html#ExecuteATaskflowWithTransferredOwnership" class="m-doc">Execute a Taskflow with Transferred Ownership</a> for more details.</p></section>
</div>
</div>
</div>
</article></main>
<divclass="m-doc-search" id="search">
<ahref="#!" onclick="return hideSearch()"></a>
<divclass="m-container">
<divclass="m-row">
<divclass="m-col-m-8 m-push-m-2">
<divclass="m-doc-search-header m-text m-small">
<div><spanclass="m-label m-default">Tab</span> / <spanclass="m-label m-default">T</span> to search, <spanclass="m-label m-default">Esc</span> to close</div>