150 lines
28 KiB
XML
150 lines
28 KiB
XML
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
|
|
<doxygen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="compound.xsd" version="1.9.1" xml:lang="en-US">
|
|
<compounddef id="DependentAsyncTasking" kind="page">
|
|
<compoundname>DependentAsyncTasking</compoundname>
|
|
<title>Asynchronous Tasking with Dependencies</title>
|
|
<tableofcontents>
|
|
<tocsect>
|
|
<name>Create a Dynamic Task Graph</name>
|
|
<reference>DependentAsyncTasking_1CreateADynamicTaskGraph</reference>
|
|
</tocsect>
|
|
<tocsect>
|
|
<name>Specify a Range of Dependent Async Tasks</name>
|
|
<reference>DependentAsyncTasking_1SpecifyARagneOfDependentAsyncTasks</reference>
|
|
</tocsect>
|
|
<tocsect>
|
|
<name>Understand the Lifetime of a Dependent Async Task</name>
|
|
<reference>DependentAsyncTasking_1UnderstandTheLifeTimeOfADependentAsyncTask</reference>
|
|
</tocsect>
|
|
<tocsect>
|
|
<name>Create a Dynamic Task Graph by Multiple Threads</name>
|
|
<reference>DependentAsyncTasking_1CreateADynamicTaskGraphByMultipleThreads</reference>
|
|
</tocsect>
|
|
<tocsect>
|
|
<name>Query the Completion Status of Dependent Async Tasks</name>
|
|
<reference>DependentAsyncTasking_1QueryTheComppletionStatusOfDependentAsyncTasks</reference>
|
|
</tocsect>
|
|
</tableofcontents>
|
|
<briefdescription>
|
|
</briefdescription>
|
|
<detaileddescription>
|
|
<para>This chapters discusses how to create a task graph dynamically using asynchronous tasks, which is extremely beneficial for workloads that want to (1) explore task graph parallelism out of dynamic control flow or (2) overlap task graph creation time with individual task execution time. We recommend that you first read <ref refid="AsyncTasking" kindref="compound">Asynchronous Tasking</ref> before digesting this chapter.</para>
|
|
<sect1 id="DependentAsyncTasking_1CreateADynamicTaskGraph">
|
|
<title>Create a Dynamic Task Graph</title>
|
|
<para>When the construct-and-run model of a task graph is not possible in your application, you can use <ref refid="classtf_1_1Executor_1aee02b63d3a91ad5ca5a1c0e71f3e128f" kindref="member">tf::Executor::dependent_async</ref> and <ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">tf::Executor::silent_dependent_async</ref> to create a task graph dynamically. This type of parallelism is also known as <emphasis>on-the-fly</emphasis> task graph parallelism, which offers great flexibility for expressing dynamic task graph parallelism. The example below dynamically creates a task graph of four dependent async tasks, <computeroutput>A</computeroutput>, <computeroutput>B</computeroutput>, <computeroutput>C</computeroutput>, and <computeroutput>D</computeroutput>, where <computeroutput>A</computeroutput> runs before <computeroutput>B</computeroutput> and <computeroutput>C</computeroutput> and <computeroutput>D</computeroutput> runs after <computeroutput>B</computeroutput> and <computeroutput>C:</computeroutput> </para>
|
|
<para><dotfile name="/home/thuang295/Code/taskflow/doxygen/images/simple.dot"></dotfile>
|
|
</para>
|
|
<para><programlisting filename=".cpp"><codeline><highlight class="normal"><ref refid="classtf_1_1Executor" kindref="compound">tf::Executor</ref><sp/>executor;</highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref><sp/>A<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){<sp/><ref refid="cpp/io/c/fprintf" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">printf</ref>(</highlight><highlight class="stringliteral">"A\n"</highlight><highlight class="normal">);<sp/>});</highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref><sp/>B<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){<sp/><ref refid="cpp/io/c/fprintf" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">printf</ref>(</highlight><highlight class="stringliteral">"B\n"</highlight><highlight class="normal">);<sp/>},<sp/>A);</highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref><sp/>C<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){<sp/><ref refid="cpp/io/c/fprintf" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">printf</ref>(</highlight><highlight class="stringliteral">"C\n"</highlight><highlight class="normal">);<sp/>},<sp/>A);</highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight><highlight class="keyword">auto</highlight><highlight class="normal"><sp/>[D,<sp/>fuD]<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1aee02b63d3a91ad5ca5a1c0e71f3e128f" kindref="member">dependent_async</ref>([](){<sp/><ref refid="cpp/io/c/fprintf" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">printf</ref>(</highlight><highlight class="stringliteral">"D\n"</highlight><highlight class="normal">);<sp/>},<sp/>B,<sp/>C);</highlight></codeline>
|
|
<codeline><highlight class="normal">fuD.get();<sp/><sp/></highlight><highlight class="comment">//<sp/>wait<sp/>for<sp/>D<sp/>to<sp/>finish,<sp/>which<sp/>in<sp/>turn<sp/>means<sp/>A,<sp/>B,<sp/>C<sp/>have<sp/>finished</highlight></codeline>
|
|
</programlisting></para>
|
|
<para>Both <ref refid="classtf_1_1Executor_1aee02b63d3a91ad5ca5a1c0e71f3e128f" kindref="member">tf::Executor::dependent_async</ref> and <ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">tf::Executor::silent_dependent_async</ref> create a task of type <ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref> to run the given function asynchronously. Additionally, <ref refid="classtf_1_1Executor_1aee02b63d3a91ad5ca5a1c0e71f3e128f" kindref="member">tf::Executor::dependent_async</ref> returns a <ulink url="https://en.cppreference.com/w/cpp/thread/future">std::future</ulink> that eventually holds the result of the execution. When returning from both calls, the executor has scheduled a worker to run the task whenever its dependencies are met. That is, task execution happens <emphasis>simultaneously</emphasis> with the creation of the task graph, which is different from constructing a Taskflow and running it from an executor, illustrated in the figure below:</para>
|
|
<para><image type="html" name="dependent_async_execution_diagram.png"></image>
|
|
</para>
|
|
<para>Since this model only allows relating a dependency from the current task to a previously created task, you need a correct topological order of graph expression. In our example, there are only two possible topological orderings, either <computeroutput>ABCD</computeroutput> or <computeroutput>ACBD</computeroutput>. The code below shows another feasible order of expressing this dynamic task graph parallelism:</para>
|
|
<para><programlisting filename=".cpp"><codeline><highlight class="normal"><ref refid="classtf_1_1Executor" kindref="compound">tf::Executor</ref><sp/>executor;</highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref><sp/>A<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){<sp/><ref refid="cpp/io/c/fprintf" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">printf</ref>(</highlight><highlight class="stringliteral">"A\n"</highlight><highlight class="normal">);<sp/>});</highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref><sp/>C<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){<sp/><ref refid="cpp/io/c/fprintf" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">printf</ref>(</highlight><highlight class="stringliteral">"C\n"</highlight><highlight class="normal">);<sp/>},<sp/>A);</highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref><sp/>B<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){<sp/><ref refid="cpp/io/c/fprintf" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">printf</ref>(</highlight><highlight class="stringliteral">"B\n"</highlight><highlight class="normal">);<sp/>},<sp/>A);</highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight><highlight class="keyword">auto</highlight><highlight class="normal"><sp/>[D,<sp/>fuD]<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1aee02b63d3a91ad5ca5a1c0e71f3e128f" kindref="member">dependent_async</ref>([](){<sp/><ref refid="cpp/io/c/fprintf" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">printf</ref>(</highlight><highlight class="stringliteral">"D\n"</highlight><highlight class="normal">);<sp/>},<sp/>B,<sp/>C);</highlight></codeline>
|
|
<codeline><highlight class="normal">fuD.get();<sp/><sp/></highlight><highlight class="comment">//<sp/>wait<sp/>for<sp/>D<sp/>to<sp/>finish,<sp/>which<sp/>in<sp/>turn<sp/>means<sp/>A,<sp/>B,<sp/>C<sp/>have<sp/>finished</highlight></codeline>
|
|
</programlisting></para>
|
|
<para>In addition to using <ulink url="https://en.cppreference.com/w/cpp/thread/future">std::future</ulink> to synchronize the execution, you can use <ref refid="classtf_1_1Executor_1ab9aa252f70e9a40020a1e5a89d485b85" kindref="member">tf::Executor::wait_for_all</ref> to wait for all scheduled tasks to finish:</para>
|
|
<para><programlisting filename=".cpp"><codeline><highlight class="normal"><ref refid="classtf_1_1Executor" kindref="compound">tf::Executor</ref><sp/>executor;</highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref><sp/>A<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){<sp/><ref refid="cpp/io/c/fprintf" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">printf</ref>(</highlight><highlight class="stringliteral">"A\n"</highlight><highlight class="normal">);<sp/>});</highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref><sp/>B<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){<sp/><ref refid="cpp/io/c/fprintf" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">printf</ref>(</highlight><highlight class="stringliteral">"B\n"</highlight><highlight class="normal">);<sp/>},<sp/>A);</highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref><sp/>C<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){<sp/><ref refid="cpp/io/c/fprintf" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">printf</ref>(</highlight><highlight class="stringliteral">"C\n"</highlight><highlight class="normal">);<sp/>},<sp/>A);</highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref><sp/>D<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){<sp/><ref refid="cpp/io/c/fprintf" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">printf</ref>(</highlight><highlight class="stringliteral">"D\n"</highlight><highlight class="normal">);<sp/>},<sp/>B,<sp/>C);</highlight></codeline>
|
|
<codeline><highlight class="normal">executor.<ref refid="classtf_1_1Executor_1ab9aa252f70e9a40020a1e5a89d485b85" kindref="member">wait_for_all</ref>();</highlight></codeline>
|
|
</programlisting></para>
|
|
</sect1>
|
|
<sect1 id="DependentAsyncTasking_1SpecifyARagneOfDependentAsyncTasks">
|
|
<title>Specify a Range of Dependent Async Tasks</title>
|
|
<para>Both <ref refid="classtf_1_1Executor_1aee02b63d3a91ad5ca5a1c0e71f3e128f" kindref="member">tf::Executor::dependent_async(F&& func, Tasks&&... tasks)</ref> and <ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">tf::Executor::silent_dependent_async(F&& func, Tasks&&... tasks)</ref> accept an arbitrary number of tasks in the dependency list. If the number of dependent tasks is unknown at programming time, such as those relying on runtime variables, you can use the following two overloads to specify dependent tasks in an iterable range <computeroutput>[first, last)</computeroutput>:</para>
|
|
<para><itemizedlist>
|
|
<listitem><para><ref refid="classtf_1_1Executor_1a01e51e564f5def845506bcf6b4bb1664" kindref="member">tf::Executor::dependent_async(F&& func, I first, I last)</ref></para>
|
|
</listitem><listitem><para><ref refid="classtf_1_1Executor_1aa9b08e47e68ae1e568f18aa7104cb9b1" kindref="member">tf::Executor::silent_dependent_async(F&& func, I first, I last)</ref></para>
|
|
</listitem></itemizedlist>
|
|
</para>
|
|
<para>The code below creates an asynchronous task that depends on <computeroutput>N</computeroutput> previously created asynchronous tasks stored in a vector, where <computeroutput>N</computeroutput> is a runtime variable:</para>
|
|
<para><programlisting filename=".cpp"><codeline><highlight class="normal"><ref refid="classtf_1_1Executor" kindref="compound">tf::Executor</ref><sp/>executor;</highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="cpp/container/vector" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">std::vector<tf::AsyncTask></ref><sp/>dependents;</highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight><highlight class="keywordflow">for</highlight><highlight class="normal">(</highlight><highlight class="keywordtype">size_t</highlight><highlight class="normal"><sp/>i=0;<sp/>i<N;<sp/>i++)<sp/>{<sp/><sp/></highlight><highlight class="comment">//<sp/>N<sp/>is<sp/>a<sp/>runtime<sp/>variable</highlight><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"><sp/><sp/>dependents.push_back(executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){}));</highlight></codeline>
|
|
<codeline><highlight class="normal">}</highlight></codeline>
|
|
<codeline><highlight class="normal">executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){},<sp/>dependents.begin(),<sp/>dependents.end());</highlight></codeline>
|
|
<codeline><highlight class="normal">executor.<ref refid="classtf_1_1Executor_1ab9aa252f70e9a40020a1e5a89d485b85" kindref="member">wait_for_all</ref>();</highlight></codeline>
|
|
</programlisting></para>
|
|
</sect1>
|
|
<sect1 id="DependentAsyncTasking_1UnderstandTheLifeTimeOfADependentAsyncTask">
|
|
<title>Understand the Lifetime of a Dependent Async Task</title>
|
|
<para>A <ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref> is a lightweight handle that retains <emphasis>shared</emphasis> ownership of a dependent async task created by an executor. This shared ownership ensures that the async task remains alive when adding it to the dependency list of another async task, thus avoiding the classical <ulink url="https://en.wikipedia.org/wiki/ABA_problem">ABA problem</ulink>.</para>
|
|
<para><programlisting filename=".cpp"><codeline><highlight class="comment">//<sp/>main<sp/>thread<sp/>retains<sp/>shared<sp/>ownership<sp/>of<sp/>async<sp/>task<sp/>A</highlight><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref><sp/>A<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){});</highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight><highlight class="comment">//<sp/>task<sp/>A<sp/>remains<sp/>alive<sp/>(i.e.,<sp/>at<sp/>least<sp/>one<sp/>ref<sp/>count<sp/>by<sp/>the<sp/>main<sp/>thread)<sp/></highlight><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight><highlight class="comment">//<sp/>when<sp/>being<sp/>added<sp/>to<sp/>the<sp/>dependency<sp/>list<sp/>of<sp/>async<sp/>task<sp/>B</highlight><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref><sp/>B<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){},<sp/>A);</highlight></codeline>
|
|
</programlisting></para>
|
|
<para>Currently, <ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref> is implemented based on the logic of C++ smart pointer <ref refid="cpp/memory/shared_ptr" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">std::shared_ptr</ref> and is considered cheap to copy or move as long as only a handful of objects own it. When a worker completes an async task, it will remove the task from the executor, decrementing the number of shared owners by one. If that counter reaches zero, the task is destroyed.</para>
|
|
</sect1>
|
|
<sect1 id="DependentAsyncTasking_1CreateADynamicTaskGraphByMultipleThreads">
|
|
<title>Create a Dynamic Task Graph by Multiple Threads</title>
|
|
<para>You can use multiple threads to create a dynamic task graph as long as the order of simultaneously creating tasks is topologically correct. The example below uses creates a dynamic task graph using three threads (including the main thread), where task <computeroutput>A</computeroutput> runs before task <computeroutput>B</computeroutput> and task <computeroutput>C:</computeroutput> </para>
|
|
<para><programlisting filename=".cpp"><codeline><highlight class="normal"><ref refid="classtf_1_1Executor" kindref="compound">tf::Executor</ref><sp/>executor;</highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight><highlight class="comment">//<sp/>main<sp/>thread<sp/>creates<sp/>a<sp/>dependent<sp/>async<sp/>task<sp/>A</highlight><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref><sp/>A<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){});</highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight><highlight class="comment">//<sp/>spawn<sp/>a<sp/>new<sp/>thread<sp/>to<sp/>create<sp/>an<sp/>async<sp/>task<sp/>B<sp/>that<sp/>runs<sp/>after<sp/>A</highlight><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="cpp/thread/thread" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">std::thread</ref><sp/>t1([&](){</highlight></codeline>
|
|
<codeline><highlight class="normal"><sp/><sp/><ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref><sp/>B<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){},<sp/>A);</highlight></codeline>
|
|
<codeline><highlight class="normal">});</highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight><highlight class="comment">//<sp/>spawn<sp/>a<sp/>new<sp/>thread<sp/>to<sp/>create<sp/>an<sp/>async<sp/>task<sp/>C<sp/>that<sp/>runs<sp/>after<sp/>A</highlight><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="cpp/thread/thread" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">std::thread</ref><sp/>t2([&](){</highlight></codeline>
|
|
<codeline><highlight class="normal"><sp/><sp/><ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref><sp/>C<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1a0e2d792f28136b8227b413d0c27d5c7f" kindref="member">silent_dependent_async</ref>([](){},<sp/>A);</highlight></codeline>
|
|
<codeline><highlight class="normal">});</highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal">executor.<ref refid="classtf_1_1Executor_1ab9aa252f70e9a40020a1e5a89d485b85" kindref="member">wait_for_all</ref>();</highlight></codeline>
|
|
<codeline><highlight class="normal">t1.join();</highlight></codeline>
|
|
<codeline><highlight class="normal">t2.join();</highlight></codeline>
|
|
</programlisting></para>
|
|
<para>Regardless of <computeroutput>t1</computeroutput> runs before or after <computeroutput>t2</computeroutput>, the resulting topological order is always correct with the graph definition, either <computeroutput>ABC</computeroutput> or <computeroutput>ACB</computeroutput>.</para>
|
|
</sect1>
|
|
<sect1 id="DependentAsyncTasking_1QueryTheComppletionStatusOfDependentAsyncTasks">
|
|
<title>Query the Completion Status of Dependent Async Tasks</title>
|
|
<para>When you create a dependent async task, you can query its completion status by <ref refid="classtf_1_1AsyncTask_1aefeefa30d7cafdfbb7dc8def542e8e51" kindref="member">tf::AsyncTask::is_done</ref>, which returns <computeroutput>true</computeroutput> upon completion or <computeroutput>false</computeroutput> otherwise. A completed dependent async task indicates that a worker has executed its associated callable.</para>
|
|
<para><programlisting filename=".cpp"><codeline><highlight class="comment">//<sp/>create<sp/>a<sp/>dependent<sp/>async<sp/>task<sp/>that<sp/>returns<sp/>100</highlight><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight><highlight class="keyword">auto</highlight><highlight class="normal"><sp/>[task,<sp/>fu]<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1aee02b63d3a91ad5ca5a1c0e71f3e128f" kindref="member">dependent_async</ref>([](){<sp/></highlight><highlight class="keywordflow">return</highlight><highlight class="normal"><sp/>100;<sp/>});</highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight><highlight class="comment">//<sp/>loops<sp/>until<sp/>the<sp/>dependent<sp/>async<sp/>task<sp/>completes</highlight><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight><highlight class="keywordflow">while</highlight><highlight class="normal">(!task.is_done());</highlight></codeline>
|
|
<codeline><highlight class="normal">assert(fu.get()<sp/>==<sp/>100);</highlight></codeline>
|
|
</programlisting></para>
|
|
<para><ref refid="classtf_1_1AsyncTask_1aefeefa30d7cafdfbb7dc8def542e8e51" kindref="member">tf::AsyncTask::is_done</ref> is useful when you need to wait on the result of a dependent async task before moving onto the next program instruction. Often, <ref refid="classtf_1_1AsyncTask" kindref="compound">tf::AsyncTask</ref> is used together with <ref refid="classtf_1_1Executor_1a0fc6eb19f168dc4a9cd0a7c6187c1d2d" kindref="member">tf::Executor::corun_until</ref> to keep a worker awake in its work-stealing loop to avoid deadlock (see <ref refid="ExecuteTaskflow_1ExecuteATaskflowFromAnInternalWorker" kindref="member">Execute a Taskflow from an Internal Worker</ref> for more details). For instance, the code below implements the famous Fibonacci sequence using recursive asynchronous tasking:</para>
|
|
<para><programlisting filename=".cpp"><codeline><highlight class="normal"><ref refid="classtf_1_1Executor" kindref="compound">tf::Executor</ref><sp/>executor;</highlight></codeline>
|
|
<codeline><highlight class="normal"><ref refid="cpp/utility/functional/function" kindref="compound" external="/home/thuang295/Code/taskflow/doxygen/cppreference-doxygen-web.tag.xml">std::function</ref><int(</highlight><highlight class="keywordtype">int</highlight><highlight class="normal">)><sp/>fibonacci;</highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight><highlight class="comment">//<sp/>calculate<sp/>the<sp/>Fibonacci<sp/>sequence:<sp/>0,<sp/>1,<sp/>1,<sp/>2,<sp/>3,<sp/>5,<sp/>8,<sp/>13,<sp/>21,<sp/>34,<sp/>55,<sp/>89</highlight><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal">fibonacci<sp/>=<sp/>[&](</highlight><highlight class="keywordtype">int</highlight><highlight class="normal"><sp/>N){</highlight></codeline>
|
|
<codeline><highlight class="normal"><sp/><sp/></highlight><highlight class="keywordflow">if</highlight><highlight class="normal"><sp/>(N<sp/><<sp/>2)<sp/>{</highlight></codeline>
|
|
<codeline><highlight class="normal"><sp/><sp/><sp/><sp/></highlight><highlight class="keywordflow">return</highlight><highlight class="normal"><sp/>N;<sp/></highlight></codeline>
|
|
<codeline><highlight class="normal"><sp/><sp/>}</highlight></codeline>
|
|
<codeline><highlight class="normal"><sp/><sp/></highlight><highlight class="keyword">auto</highlight><highlight class="normal"><sp/>[t1,<sp/>fu1]<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1aee02b63d3a91ad5ca5a1c0e71f3e128f" kindref="member">dependent_async</ref>(std::bind(fibonacci,<sp/>N-1));</highlight></codeline>
|
|
<codeline><highlight class="normal"><sp/><sp/></highlight><highlight class="keyword">auto</highlight><highlight class="normal"><sp/>[t2,<sp/>fu2]<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1aee02b63d3a91ad5ca5a1c0e71f3e128f" kindref="member">dependent_async</ref>(std::bind(fibonacci,<sp/>N-2));</highlight></codeline>
|
|
<codeline><highlight class="normal"><sp/><sp/>executor.<ref refid="classtf_1_1Executor_1a0fc6eb19f168dc4a9cd0a7c6187c1d2d" kindref="member">corun_until</ref>([&](){<sp/></highlight><highlight class="keywordflow">return</highlight><highlight class="normal"><sp/>t1.is_done()<sp/>&&<sp/>t2.is_done();<sp/>});</highlight></codeline>
|
|
<codeline><highlight class="normal"><sp/><sp/></highlight><highlight class="keywordflow">return</highlight><highlight class="normal"><sp/>fu1.get()<sp/>+<sp/>fu2.get();</highlight></codeline>
|
|
<codeline><highlight class="normal">};</highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight></codeline>
|
|
<codeline><highlight class="normal"></highlight><highlight class="keyword">auto</highlight><highlight class="normal"><sp/>[task,<sp/>fib11]<sp/>=<sp/>executor.<ref refid="classtf_1_1Executor_1aee02b63d3a91ad5ca5a1c0e71f3e128f" kindref="member">dependent_async</ref>(std::bind(fibonacci,<sp/>11));</highlight></codeline>
|
|
<codeline><highlight class="normal">assert(fib11<sp/>==<sp/>89);<sp/><sp/></highlight><highlight class="comment">//<sp/>the<sp/>11-th<sp/>Fibonacci<sp/>number<sp/>is<sp/>89</highlight></codeline>
|
|
</programlisting> </para>
|
|
</sect1>
|
|
</detaileddescription>
|
|
<location file="doxygen/cookbook/dependent_async_tasking.dox"/>
|
|
</compounddef>
|
|
</doxygen>
|