{"id":2823,"date":"2008-01-31T14:55:27","date_gmt":"2008-01-31T14:55:27","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/pfxteam\/2008\/01\/31\/recursion-and-concurrency\/"},"modified":"2008-01-31T14:55:27","modified_gmt":"2008-01-31T14:55:27","slug":"recursion-and-concurrency","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/recursion-and-concurrency\/","title":{"rendered":"Recursion and Concurrency"},"content":{"rendered":"<p>When&nbsp;teaching recursion in an introductory computer science course, one of the most common examples used involves a tree data structure.&nbsp; Trees are useful in this regard as they are simple and recursive in nature, with a tree&#8217;s children also being trees, and allow for teaching different kinds of traversals (in-order, pre-order, post-order, level-order, and so forth).&nbsp; But when these introductory concepts meet multithreading, the concepts are no longer so simple, at least not with the tools available in mainstream languages today like C#, C++, and Java. Consider a simple Tree data structure:<\/p>\n<blockquote>\n<p class=\"MsoNormal\" style=\"margin-bottom: 0pt;line-height: normal\"><span style=\"font-size: 10pt;color: blue;font-family: consolas\">class<\/span><span style=\"font-size: 10pt;font-family: consolas\"> <span style=\"color: #2b91af\">Tree<\/span>&lt;T&gt;<br><\/span><span style=\"font-size: 10pt;font-family: consolas\">{<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">public<\/span> <span style=\"color: #2b91af\">Tree<\/span>&lt;T&gt; Left, Right; <\/span><span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/ children<\/span><span style=\"font-size: 10pt;font-family: consolas\"><br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">public<\/span> T Data; <span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/ data for this node&nbsp;<\/span><br><\/span><span style=\"font-size: 10pt;line-height: 115%;font-family: consolas\">}<\/span><\/p>\n<\/blockquote>\n<p>Let&#8217;s say we want to execute an Action&lt;T&gt; for each datum stored in the tree, and let&#8217;s assume I don&#8217;t care about order (introducing parallelism could be questionable if I did).&nbsp; That&#8217;s straightforward to do sequentially and recursively:<\/p>\n<blockquote>\n<p class=\"MsoNormal\" style=\"margin-bottom: 0pt;line-height: normal\"><span style=\"font-size: 10pt;color: blue;font-family: consolas\">public<\/span><span style=\"font-size: 10pt;font-family: consolas\"> <span style=\"color: blue\">static<\/span> <span style=\"color: blue\">void<\/span> Process&lt;T&gt;(<span style=\"color: #2b91af\">Tree<\/span>&lt;T&gt; tree, <span style=\"color: #2b91af\">Action<\/span>&lt;T&gt; action)<br><\/span><span style=\"font-size: 10pt;font-family: consolas\">{<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">if<\/span> (tree == <span style=\"color: blue\">null<\/span>) <span style=\"color: blue\">return<\/span>;<\/p>\n<p><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Process the current node, then the left,&nbsp;then the right<\/span><br>&nbsp;&nbsp;&nbsp; <\/span>action(tree.Data);<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>Process(tree.Left, action);<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>Process(tree.Right, action);<br><\/span><span style=\"font-size: 10pt;line-height: 115%;font-family: consolas\">}<\/span><\/p>\n<\/blockquote>\n<p>I could also do so without recursion by maintaining&nbsp;an explicit stack (or queue, or some other data structure with different ordering guarantees):<\/p>\n<blockquote>\n<p class=\"MsoNormal\" style=\"margin-bottom: 0pt;line-height: normal\"><span style=\"font-size: 10pt;color: blue;font-family: consolas\">public<\/span><span style=\"font-size: 10pt;font-family: consolas\"> <span style=\"color: blue\">static<\/span> <span style=\"color: blue\">void<\/span> Process&lt;T&gt;(<span style=\"color: #2b91af\">Tree<\/span>&lt;T&gt; tree, <span style=\"color: #2b91af\">Action<\/span>&lt;T&gt; action)<br><\/span><span style=\"font-size: 10pt;font-family: consolas\">{<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">if<\/span> (tree == <span style=\"color: blue\">null<\/span>) <span style=\"color: blue\">return<\/span>;<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">var<\/span> toExplore = <span style=\"color: blue\">new<\/span> <span style=\"color: #2b91af\">Stack<\/span>&lt;<span style=\"color: #2b91af\">Tree<\/span>&lt;T&gt;&gt;();<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span><br>&nbsp;&nbsp;&nbsp; <span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Start with the root node<\/span><br>&nbsp;&nbsp;&nbsp; <\/span>toExplore.Push(tree);<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">while<\/span> (toExplore.Count &gt; 0)<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Grab the next node, process it, and push its children<\/span><\/span><span style=\"font-size: 10pt;font-family: consolas\"><br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">var<\/span> current = toExplore.Pop();<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>action(current.Data);<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span><span style=\"color: blue\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if<\/span> (current.Left != <span style=\"color: blue\">null<\/span>) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>toExplore.Push(current.Left);<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span><span style=\"color: blue\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if<\/span> (current.Right != <span style=\"color: blue\">null<\/span>) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>toExplore.Push(current.Right);<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>}<br><\/span><span style=\"font-size: 10pt;line-height: 115%;font-family: consolas\">}<\/span><\/p>\n<\/blockquote>\n<p class=\"MsoNormal\">\n<p>Now, let&#8217;s assume the action we&#8217;re performing on each node of the tree is independent, relatively expensive and\/or that the tree is relatively large, and as such we want to process the tree in parallel (we&#8217;re of course also assuming that the action delegate is thread-safe), meaning that we want multiple threads each running the action delegate on distinct tree nodes.&nbsp; How do we do this with what we have in .NET today?<\/p>\n<\/p>\n<p class=\"MsoNormal\">\n<p>There are multiple approaches, some more valid than others.&nbsp; The first thing someone might try is to follow the original recursive implementation but using the ThreadPool, which could look something like this:<\/p>\n<\/p>\n<blockquote>\n<p class=\"MsoNormal\" style=\"margin-bottom: 0pt;line-height: normal\"><span style=\"font-size: 10pt;color: blue;font-family: consolas\">public<\/span><span style=\"font-size: 10pt;font-family: consolas\"> <span style=\"color: blue\">static<\/span> <span style=\"color: blue\">void<\/span> Process&lt;T&gt;(<span style=\"color: #2b91af\">Tree<\/span>&lt;T&gt; tree, <span style=\"color: #2b91af\">Action<\/span>&lt;T&gt; action)<br><\/span><span style=\"font-size: 10pt;font-family: consolas\">{<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">if<\/span> (tree == <span style=\"color: blue\">null<\/span>) <span style=\"color: blue\">return<\/span>;<\/p>\n<p>&nbsp;&nbsp;&nbsp; <span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Use an event to prevent this method from<br>&nbsp;&nbsp;&nbsp; \/\/ returning until its children have completed<\/span><\/span><span style=\"font-size: 10pt;font-family: consolas\"><br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">using<\/span> (<span style=\"color: blue\">var <\/span>mre = <span style=\"color: blue\">new<\/span> <span style=\"color: #2b91af\">ManualResetEvent<\/span>(<span style=\"color: blue\">false<\/span>))<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Process the left&nbsp;child asynchronously<\/span><\/span><span style=\"font-size: 10pt;font-family: consolas\"><br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #2b91af\">ThreadPool<\/span>.QueueUserWorkItem(<span style=\"color: blue\">delegate<br><\/span><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>{<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>Process(tree.Left, action);<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>mre.Set();<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>});<\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Process current node and right child synchronously<\/span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;action(tree.Data);<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>Process(tree.Right, action);<\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Wait for the left child<\/span><\/span><span style=\"font-size: 10pt;font-family: consolas\"><br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>mre.WaitOne();<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>}<br><\/span><span style=\"font-size: 10pt;line-height: 115%;font-family: consolas\">}<\/span><\/p>\n<\/blockquote>\n<p class=\"MsoNormal\">\n<p>The idea behind this implementation is to, given a node, spin up a work item to process that node&#8217;s left child in parallel with the current node, and then process the current node&#8217;s data as well as its right child.&nbsp; Of course, I could be losing out on some parallelism here as I delay processing of the right child until I&#8217;m done processing the current data.&nbsp; So we modify it slightly:<\/p>\n<\/p>\n<blockquote>\n<p class=\"MsoNormal\" style=\"margin-bottom: 0pt;line-height: normal\"><span style=\"font-size: 10pt;color: blue;font-family: consolas\">public<\/span><span style=\"font-size: 10pt;font-family: consolas\"> <span style=\"color: blue\">static<\/span> <span style=\"color: blue\">void<\/span> Process&lt;T&gt;(<span style=\"color: #2b91af\">Tree<\/span>&lt;T&gt; tree, <span style=\"color: #2b91af\">Action<\/span>&lt;T&gt; action)<br><\/span><span style=\"font-size: 10pt;font-family: consolas\">{<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">if<\/span> (tree == <span style=\"color: blue\">null<\/span>) <span style=\"color: blue\">return<\/span>;<\/p>\n<p>&nbsp;&nbsp;&nbsp; <span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Use an event to wait for the children<\/span><br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">using<\/span> (<span style=\"color: blue\">var<\/span> mre = <span style=\"color: blue\">new<\/span> <span style=\"color: #2b91af\">ManualResetEvent<\/span>(<span style=\"color: blue\">false<\/span>))<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>{<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">int<\/span> count = 2;<\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Process the left child asynchronously<\/span><br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #2b91af\">ThreadPool<\/span>.QueueUserWorkItem(<span style=\"color: blue\">delegate<br><\/span><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>{<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>Process(tree.Left, action);<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">if<\/span> (<span style=\"color: #2b91af\">Interlocked<\/span>.Decrement(<span style=\"color: blue\">ref<\/span> count) == 0) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mre.Set();<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>});<\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Process the right child asynchronously<\/span><\/span><span style=\"font-size: 10pt;font-family: consolas\"><br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #2b91af\">ThreadPool<\/span>.QueueUserWorkItem(<span style=\"color: blue\">delegate<br><\/span><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>{<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>Process(tree.Right, action);<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">if<\/span> (<span style=\"color: #2b91af\">Interlocked<\/span>.Decrement(<span style=\"color: blue\">ref<\/span> count) == 0) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mre.Set();<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>});<\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Process the current node synchronously<\/span><br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>action(tree.Data);<\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Wait for the children<\/span><br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>mre.WaitOne();<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>}<br><\/span><span style=\"font-size: 10pt;line-height: 115%;font-family: consolas\">}<\/span><\/p>\n<\/blockquote>\n<p class=\"MsoNormal\">\n<p>I&#8217;ve now fixed that issue, such that both the left and the right children could potentially be processed in parallel with the current node, but that was by far not the worst problem.&nbsp; For starters, I&#8217;m creating a ManualResetEvent for every node in the tree&#8230; that&#8217;s expensive.&nbsp; ManualResetEvent is a thin wrapper around a Win32 kernel event primitive, so creating one of these things requires kernel transitions, as does setting and waiting on one.&nbsp; Next, every time I process a node, I block waiting for its children to complete.&nbsp; And as the processing of a node (all but the root) is happening on a thread from the ThreadPool, I&#8217;m blocking ThreadPool threads.&nbsp; If a ThreadPool thread gets blocked, the ThreadPool will need to inject additional threads in order to process the remaining work items, and thus this implementation will require approximately one thread from the pool per node in the tree.&nbsp; That&#8217;s a lot of threads!&nbsp; And that carries with it some serious problems.&nbsp; By default, a thread in .NET has a megabyte of stack space committed for it, so each thread burns a megabyte of (virtual) memory.&nbsp; The ThreadPool also throttles the creation of additional threads, such that introducing a new thread (once the number of pool threads equals the number of processors) will take 500 ms.&nbsp; For a tree of 250 nodes, that means its processing will take close to 2 minutes, purely for the overhead of creating threads, nevermind the actual processing of the nodes.&nbsp; And worse, there is a maximum number of threads in the pool: in .NET, the ThreadPool has a limited number of threads, by default 25 per processor in .NET 1.x\/2.0 and 250 per processor in .NET 2.0 SP1.&nbsp; If the pool reaches the maximum, no new threads will be created, and thus this implementation could deadlock.&nbsp; Parent nodes will be waiting for their child nodes to complete, but the child nodes can&#8217;t be processed until their parent nodes complete and relinquish a thread from the pool to execute.<\/p>\n<\/p>\n<p class=\"MsoNormal\">\n<p>Obviously, we need a better implementation. Next up, we can walk the tree sequentially, queuing up a work item in the pool for each node in the tree.&nbsp; Here we take that approach based on the recursive implementation of a tree walk:<\/p>\n<\/p>\n<blockquote>\n<p class=\"MsoNormal\" style=\"margin-bottom: 0pt;line-height: normal\"><span style=\"font-size: 10pt;color: blue;font-family: consolas\">public<\/span><span style=\"font-size: 10pt;font-family: consolas\"> <span style=\"color: blue\">static<\/span> <span style=\"color: blue\">void<\/span> Process&lt;T&gt;(<span style=\"color: #2b91af\">Tree<\/span>&lt;T&gt; tree, <span style=\"color: #2b91af\">Action<\/span>&lt;T&gt; action)<br><\/span><span style=\"font-size: 10pt;font-family: consolas\">{<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">if<\/span> (tree == <span style=\"color: blue\">null<\/span>) <span style=\"color: blue\">return<\/span>;<\/p>\n<p>&nbsp;&nbsp;&nbsp; <span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Use an event to wait for all of the nodes to complete<\/span><br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">using<\/span> (<span style=\"color: blue\">var<\/span> mre = <span style=\"color: blue\">new<\/span> <span style=\"color: #2b91af\">ManualResetEvent<\/span>(<span style=\"color: blue\">false<\/span>))<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>{<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">int<\/span> count = 1;<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Recursive delegate to walk the tree<\/span><br><\/span><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #2b91af\">Action<\/span>&lt;<span style=\"color: #2b91af\">Tree<\/span>&lt;T&gt;&gt; processNode = <span style=\"color: blue\">null<\/span>;<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>processNode = node =&gt;<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>{<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">if<\/span> (node == <span style=\"color: blue\">null<\/span>) <span style=\"color: blue\">return<\/span>;<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><\/p>\n<p>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Asynchronously&nbsp;run the action on&nbsp;the current node<\/span><br><\/p>\n<p><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #2b91af\">Interlocked<\/span>.Increment(<span style=\"color: blue\">ref<\/span> count);<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: #2b91af\">ThreadPool<\/span>.QueueUserWorkItem(<span style=\"color: blue\">delegate<br><\/span><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>{<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>action(node.Data);<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span style=\"color: blue\">if<\/span> (<span style=\"color: #2b91af\">Interlocked<\/span>.Decrement(<span style=\"color: blue\">ref<\/span> count) == 0) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mre.Set();<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>});<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><\/p>\n<p>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Process the&nbsp;children<\/span><br><\/p>\n<p><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>processNode(node.Left);<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>processNode(node.Right);<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>};<br><\/span><span style=\"font-size: 10pt;font-family: consolas\"><\/p>\n<p>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style=\"font-size: 10pt;color: green;line-height: 115%;font-family: consolas\">\/\/&nbsp;Start off with the root node<\/span><br><\/p>\n<p><\/span><span style=\"font-size: 10pt;font-family: consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/p>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>When&nbsp;teaching recursion in an introductory computer science course, one of the most common examples used involves a tree data structure.&nbsp; Trees are useful in this regard as they are simple and recursive in nature, with a tree&#8217;s children also being trees, and allow for teaching different kinds of traversals (in-order, pre-order, post-order, level-order, and so [&hellip;]<\/p>\n","protected":false},"author":360,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[7908],"tags":[7911,7909,7913,7910,7912,7914],"class_list":["post-2823","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pfxteam","tag-code-samples","tag-parallel-extensions","tag-parallelism-blockers","tag-plinq","tag-task-parallel-library","tag-threadpool"],"acf":[],"blog_post_summary":"<p>When&nbsp;teaching recursion in an introductory computer science course, one of the most common examples used involves a tree data structure.&nbsp; Trees are useful in this regard as they are simple and recursive in nature, with a tree&#8217;s children also being trees, and allow for teaching different kinds of traversals (in-order, pre-order, post-order, level-order, and so [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/2823","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/360"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=2823"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/2823\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=2823"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=2823"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=2823"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}