<!-- 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: Executor </div></div>
</div><!--header-->
<divclass="contents">
<divclass="textblock"><p>After you create a task dependency graph, you need to submit it to threads for execution. In this chapter, we will show you how to execute a task dependency graph.</p>
<p>To execute a taskflkow, 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 is a <em>thread-safe</em> object that manages a set of worker threads and executes tasks through an efficient <em>work-stealing</em> algorithm. Issuing a call to run a taskflow creates a <em>topology</em>, a data structure to keep track of the execution status of a running graph. <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 with <code>N</code> worker threads. The default value is <aclass="elRef" doxygen="/home/tsung-wei/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 to run multiple taskflows where each taskflow represents a part 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/tsung-wei/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/tsung-wei/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/tsung-wei/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/tsung-wei/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/tsung-wei/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.wait(); <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/tsung-wei/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>; }).wait();</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/tsung-wei/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>; }).wait();</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/tsung-wei/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/thread/future/wait.html">std::future::wait</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 <aclass="el" href="classtf_1_1Executor.html#ab9aa252f70e9a40020a1e5a89d485b85" title="wait for all pending graphs to complete ">tf::Executor::wait_for_all</a> 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 the same taskflow will automatically <em>synchronize</em> to a sequential chain of executions in the order of 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/tsung-wei/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/tsung-wei/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>
<h1><aclass="anchor" id="C2_ThreadSafety"></a>
Thread Safety</h1>
<p><aclass="el" href="classtf_1_1Executor.html" title="The executor class to run a taskflow graph. ">tf::Executor</a> is <em>thread-safe</em>. Touching an executor from multiple threads is acceptable. You can have multiple threads call the same executor to run different taskflows.</p>
<divclass="fragment"><divclass="line">1: <aclass="code" href="classtf_1_1Executor.html">tf::Executor</a> executor;</div><divclass="line">2:</div><divclass="line">3: <spanclass="keywordflow">for</span>(<spanclass="keywordtype">int</span> i=0; i<10; ++i) {</div><divclass="line">4: <aclass="codeRef" doxygen="/home/tsung-wei/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/thread/thread.html">std::thread</a>([i, &](){</div><divclass="line">5: <spanclass="comment">// ... modify my taskflow</span></div><divclass="line">6: executor.<aclass="code" href="classtf_1_1Executor.html#a81f35d5b0a20ac0646447eb80d97c0aa">run</a>(taskflows[i]); <spanclass="comment">// run my taskflow</span></div><divclass="line">7: }).detach();</div><divclass="line">8: }</div></div><!-- fragment --><h1><aclass="anchor" id="C2_MonitorThreadActivities"></a>
Monitor Thread Activities</h1>
<p>Inspecting the thread activities is very important for performance analysis. It allows you to know when each task starts and ends participating in the task scheduling. Cpp-Taskflow provides a default observer class <aclass="el" href="classtf_1_1ExecutorObserver.html" title="Default executor observer to dump the execution timelines. ">tf::ExecutorObserver</a> for this purpose. The following example shows how to create an observer from an executor.</p>
<divclass="fragment"><divclass="line"><aclass="code" href="classtf_1_1Executor.html">tf::Executor</a> executor;</div><divclass="line"><aclass="code" href="classtf_1_1ExecutorObserver.html">tf::ExecutorObserver</a>* observer = executor.<aclass="code" href="classtf_1_1Executor.html#a7f43cde72d3e0a17e2d006cbe7a41ff3">make_observer</a><<aclass="code" href="classtf_1_1ExecutorObserver.html">tf::ExecutorObserver</a>>();</div></div><!-- fragment --><p>Note that each executor can only have an observer at a time. An observer will automatically record the start and end timestamps of each executed task. Users can query, dump or remove the timestamps through the <aclass="el" href="classtf_1_1ExecutorObserver.html#a93d51307198abb9a1fc00a14904c3dd0" title="get the number of total tasks in the observer ">tf::ExecutorObserver::num_tasks</a>, <aclass="el" href="classtf_1_1ExecutorObserver.html#a20f77a06e0f10dc67f7a5742add5b00a" title="dump the timelines in JSON format to an ostream ">tf::ExecutorObserver::dump</a> and <aclass="el" href="classtf_1_1ExecutorObserver.html#adc78a004eaa25022a20fd16a35f607ce" title="clear the timeline data ">tf::ExecutorObserver::clear</a> methods.</p>
<divclass="fragment"><divclass="line"> 1: <aclass="code" href="classtf_1_1Executor.html">tf::Executor</a> executor;</div><divclass="line"> 2: <aclass="code" href="classtf_1_1ExecutorObserver.html">tf::ExecutorObserver</a>* observer = executor.<aclass="code" href="classtf_1_1Executor.html#a7f43cde72d3e0a17e2d006cbe7a41ff3">make_observer</a><<aclass="code" href="classtf_1_1ExecutorObserver.html">tf::ExecutorObserver</a>>();</div><divclass="line"> 3:</div><divclass="line"> 4: executor.<aclass="code" href="classtf_1_1Executor.html#a81f35d5b0a20ac0646447eb80d97c0aa">run</a>(taskflow).get(); <spanclass="comment">// do something</span></div><divclass="line"> 5:</div><divclass="line"> 6: <spanclass="comment">// Query the total number of tasks (number of timestamp pairs)</span></div><divclass="line"> 7: <spanclass="keyword">auto</span> num_tasks = observer-><aclass="code" href="classtf_1_1ExecutorObserver.html#a93d51307198abb9a1fc00a14904c3dd0">num_tasks</a>();</div><divclass="line"> 8:</div><divclass="line"> 9: <spanclass="comment">// Dump the timeline data in JSON format </span></div><divclass="line">10: <aclass="codeRef" doxygen="/home/tsung-wei/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/string/basic_string.html">std::string</a> timelines_in_json = observer-><aclass="code" href="classtf_1_1ExecutorObserver.html#a20f77a06e0f10dc67f7a5742add5b00a">dump</a>();</div><divclass="line">11: </div><divclass="line">12: <spanclass="comment">// Clear the timeline data</span></div><divclass="line">13: observer-><aclass="code" href="classtf_1_1ExecutorObserver.html#adc78a004eaa25022a20fd16a35f607ce">clear</a>();</div></div><!-- fragment --><p>Debrief:</p>
<ul>
<li>Line 2-4 creates an observer and a task dependency graph with four tasks and submits the tasks to execution. </li>
<li>Line 7 query the total number of tasks (number of timestamp pair) through observer </li>
<li>Line 10 dump the timestamps to a <aclass="elRef" doxygen="/home/tsung-wei/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/string/basic_string.html">std::string</a> in JSON format </li>
<li>Line 13 remove all timestamps in the observer</li>
</ul>
<p>You can visualize the timeline data in a Chrome browser:</p>
<ul>
<li>Step 1: save the JSON timeline data to a file </li>
<li>Step 2: launch the Chrome browser and open a tab with the url: chrome://tracing </li>
<p>Tasks will be categorized by the executing thread and each task is named with <em>i_j</em> where <em>i</em> is the thread id and <em>j</em> is the task number. You can pan or zoom in/out the timeline to get a detailed view.</p>
<p>You can derive your own observer from the base interface class <aclass="el" href="classtf_1_1ExecutorObserverInterface.html" title="The interface class for creating an executor observer. ">tf::ExecutorObserverInterface</a> to customize the observing methods. </p>
</div></div><!-- contents -->
</div><!-- doc-content -->
<!-- start footer part -->
<divid="nav-path" class="navpath"><!-- id is needed for treeview function! -->