<!-- iframe showing the search results (closed by default) -->
<divid="MSearchResultsWindow">
<iframesrc="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<divclass="header">
<divclass="headertitle">
<divclass="title">C2: Execute a Taskflow </div></div>
</div><!--header-->
<divclass="contents">
<divclass="textblock"><p>After you create a task dependency graph, you need to dispatch it to threads for execution. In this chapter, we will show you how to execute a task dependency graph.</p>
<h1><aclass="anchor" id="GraphAndTopology"></a>
Graph and Topology</h1>
<p>Each taskflow object has exactly one graph at a time that represents a set of dependent tasks constructed so far. To execute a graph, you need to create an <em>executor</em> from <aclass="el" href="classtf_1_1Executor.html" title="The executor class to run a taskflow graph. ">tf::Executor</a>. An executor manages a set of worker threads and schedules task execution through an efficient <em>work-stealing</em> algorithm. Issuing a call to run a taskflow creates a <em>topology</em>. A topology is a data structure wrapping around a running graph. Each executor object has a list of topologies to keep track of the execution status of running and finished graphs. Users can retrieve this information for graph inspection and debugging.</p>
<p>The first step is to create an executor to run a taskflow. <aclass="el" href="classtf_1_1Executor.html" title="The executor class to run a taskflow graph. ">tf::Executor</a> takes an unsigned integer to construct an executor of <code>N</code> worker threads. The default value is <aclass="elRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/thread/thread/hardware_concurrency.html">std::thread::hardware_concurrency</a>.</p>
<divclass="fragment"><divclass="line"><aclass="code" href="classtf_1_1Executor.html">tf::Executor</a> executor1; <spanclass="comment">// create an executor of std::thread::hardware_concurrency worker threads</span></div><divclass="line"><aclass="code" href="classtf_1_1Executor.html">tf::Executor</a> executor2(4); <spanclass="comment">// create an executor of 4 worker threads</span></div></div><!-- fragment --><p>An executor can be reused to execute multiple taskflows. In most workloads, you may need only one executor but multiple taskflows to represent parts of a parallel decomposition.</p>
<p><aclass="el" href="classtf_1_1Executor.html" title="The executor class to run a taskflow graph. ">tf::Executor</a> provides a set of <code>run_*</code> methods, <aclass="el" href="classtf_1_1Executor.html#a81f35d5b0a20ac0646447eb80d97c0aa" title="runs the taskflow once ">tf::Executor::run</a>, <aclass="el" href="classtf_1_1Executor.html#adca6cd0ce1bd7e6fa2ed2a55c9ae15e6" title="runs the taskflow for N times ">tf::Executor::run_n</a>, and <aclass="el" href="classtf_1_1Executor.html#a8acf7515e8e8fdda366ace68bcd65aa6" title="runs the taskflow multiple times until the predicate becomes true and then invokes a callback ...">tf::Executor::run_until</a> to run a taskflow for one time, multiple times, or until a given predicate evaluates to true. All methods accept an optional callback to invoke after the execution completes, and return a <aclass="elRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/thread/future.html">std::future</a> for users to access the execution status. The code below shows several ways to run a taskflow.</p>
<divclass="fragment"><divclass="line"> 1: <spanclass="comment">// Declare an executor and a taskflow</span></div><divclass="line"> 2: <aclass="code" href="classtf_1_1Executor.html">tf::Executor</a> executor;</div><divclass="line"> 3: <aclass="code" href="classtf_1_1Taskflow.html">tf::Taskflow</a> taskflow;</div><divclass="line"> 4:</div><divclass="line"> 5: <spanclass="comment">// Add three tasks into the taskflow</span></div><divclass="line"> 6: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> A = taskflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] () { <aclass="codeRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/io/basic_ostream.html">std::cout</a> << <spanclass="stringliteral">"This is TaskA\n"</span>; });</div><divclass="line"> 7: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> B = taskflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] () { <aclass="codeRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/io/basic_ostream.html">std::cout</a> << <spanclass="stringliteral">"This is TaskB\n"</span>; });</div><divclass="line"> 8: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> C = taskflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([] () { <aclass="codeRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/io/basic_ostream.html">std::cout</a> << <spanclass="stringliteral">"This is TaskC\n"</span>; });</div><divclass="line"> 9: </div><divclass="line">10: <spanclass="comment">// Build precedence between tasks</span></div><divclass="line">11: A.<aclass="code" href="classtf_1_1Task.html#a8c78c453295a553c1c016e4062da8588">precede</a>(B, C); </div><divclass="line">12: </div><divclass="line">13: <aclass="codeRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/thread/future.html">std::future<void></a> fu = executor.<aclass="code" href="classtf_1_1Executor.html#a81f35d5b0a20ac0646447eb80d97c0aa">run</a>(taskflow);</div><divclass="line">14: fu.get(); <spanclass="comment">// block until the execution completes</span></div><divclass="line">15:</div><divclass="line">16: executor.<aclass="code" href="classtf_1_1Executor.html#a81f35d5b0a20ac0646447eb80d97c0aa">run</a>(taskflow, [](){ <aclass="codeRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/io/basic_ostream.html">std::cout</a> << <spanclass="stringliteral">"end of one execution\n"</span>; }).<spanclass="keyword">get</span>();</div><divclass="line">17: executor.<aclass="code" href="classtf_1_1Executor.html#adca6cd0ce1bd7e6fa2ed2a55c9ae15e6">run_n</a>(taskflow, 4);</div><divclass="line">18: executor.<aclass="code" href="classtf_1_1Executor.html#ab9aa252f70e9a40020a1e5a89d485b85">wait_for_all</a>(); <spanclass="comment">// block until all associated executions finish</span></div><divclass="line">19: executor.<aclass="code" href="classtf_1_1Executor.html#adca6cd0ce1bd7e6fa2ed2a55c9ae15e6">run_n</a>(taskflow, 4, [](){ <aclass="codeRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/io/basic_ostream.html">std::cout</a> << <spanclass="stringliteral">"end of four executions\n"</span>; }).<spanclass="keyword">get</span>();</div><divclass="line">20: executor.<aclass="code" href="classtf_1_1Executor.html#a8acf7515e8e8fdda366ace68bcd65aa6">run_until</a>(taskflow, [<spanclass="keywordtype">int</span> cnt=0] () <spanclass="keyword">mutable</span> { <spanclass="keywordflow">return</span> (++cnt == 10); });</div></div><!-- fragment --><p>Debrief:</p>
<ul>
<li>Line 6-8 creates a taskflow of three tasks A, B, and C </li>
<li>Line 13-14 runs the taskflow once and use <aclass="elRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/thread/future/get.html">std::future::get</a> to wait for completion </li>
<li>Line 16 runs the taskflow once with a callback to invoke when the execution finishes </li>
<li>Line 17-18 runs the taskflow four times and use tf::Taskflow::wait_for_all to wait for completion </li>
<li>Line 19 runs the taskflow four times and invokes a callback at the end of the forth execution </li>
<li>Line 20 keeps running the taskflow until the predicate returns true</li>
</ul>
<p>Issuing multiple runs on a same taskflow will automatically <em>synchronize</em> to a sequential chain of executions following the order of the run calls.</p>
<divclass="fragment"><divclass="line">executor.<aclass="code" href="classtf_1_1Executor.html#a81f35d5b0a20ac0646447eb80d97c0aa">run</a>(taskflow); <spanclass="comment">// execution 1</span></div><divclass="line">executor.<aclass="code" href="classtf_1_1Executor.html#adca6cd0ce1bd7e6fa2ed2a55c9ae15e6">run_n</a>(taskflow, 10); <spanclass="comment">// execution 2</span></div><divclass="line">executor.<aclass="code" href="classtf_1_1Executor.html#a81f35d5b0a20ac0646447eb80d97c0aa">run</a>(taskflow); <spanclass="comment">// execution 3</span></div><divclass="line">executor.<aclass="code" href="classtf_1_1Executor.html#ab9aa252f70e9a40020a1e5a89d485b85">wait_for_all</a>(); <spanclass="comment">// execution 1 -> execution 2 -> execution 3</span></div></div><!-- fragment --><p>A key point to notice is a running taskflow must remain alive during its execution. It is your responsibility to ensure a taskflow not being destructed when it is running. For example, the code below can result undefined behavior.</p>
<divclass="fragment"><divclass="line"><aclass="code" href="classtf_1_1Executor.html">tf::Executor</a> executor; <spanclass="comment">// create an executor</span></div><divclass="line"></div><divclass="line"><spanclass="comment">// create a taskflow whose lifetime is restricted by the scope</span></div><divclass="line">{</div><divclass="line"><aclass="code" href="classtf_1_1Taskflow.html">tf::Taskflow</a> taskflow;</div><divclass="line"></div><divclass="line"><spanclass="comment">// add tasks to the taskflow</span></div><divclass="line"><spanclass="comment">// ... </span></div><divclass="line"></div><divclass="line"><spanclass="comment">// run the taskflow</span></div><divclass="line"> executor.<aclass="code" href="classtf_1_1Executor.html#a81f35d5b0a20ac0646447eb80d97c0aa">run</a>(f);</div><divclass="line"></div><divclass="line">} <spanclass="comment">// at this point, taskflow might get destructed while it is running, resulting in defined behavior</span></div></div><!-- fragment --><p>Similarly, you should avoid touching a taskflow while it is running.</p>
<divclass="fragment"><divclass="line"><aclass="code" href="classtf_1_1Taskflow.html">tf::Taskflow</a> taskflow;</div><divclass="line"></div><divclass="line"><spanclass="comment">// Add tasks into the taskflow</span></div><divclass="line"><spanclass="comment">// ...</span></div><divclass="line"></div><divclass="line"><spanclass="comment">// Declare an executor</span></div><divclass="line"><aclass="code" href="classtf_1_1Executor.html">tf::Executor</a> executor;</div><divclass="line"></div><divclass="line"><aclass="codeRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/thread/future.html">std::future<void></a> future = taskflow.run(f); <spanclass="comment">// non-blocking return</span></div><divclass="line"></div><divclass="line"><spanclass="comment">// alter the taskflow while running leads to undefined behavior </span></div><divclass="line">f.<aclass="code" href="classtf_1_1FlowBuilder.html#a4d52a7fe2814b264846a2085e931652c">emplace</a>([](){ <aclass="codeRef" doxygen="/home/twhuang/PhD/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/io/basic_ostream.html">std::cout</a> << <spanclass="stringliteral">"Add a new task\n"</span>; });</div></div><!-- fragment --><p>A rule of thumb is to always keep a taskflow alive in your function scope while it is participating in an execution. </p>
</div></div><!-- contents -->
</div><!-- doc-content -->
<!-- start footer part -->
<divid="nav-path" class="navpath"><!-- id is needed for treeview function! -->