{"id":55968,"date":"2009-10-27T11:02:00","date_gmt":"2009-10-27T11:02:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/pfxteam\/2009\/10\/27\/whats-new-in-beta-2-for-the-task-parallel-library-part-23\/"},"modified":"2009-10-27T11:02:00","modified_gmt":"2009-10-27T11:02:00","slug":"whats-new-in-beta-2-for-the-task-parallel-library-part-23","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/whats-new-in-beta-2-for-the-task-parallel-library-part-23\/","title":{"rendered":"What\u2019s new in Beta 2 for the Task Parallel Library? (Part 2\/3)"},"content":{"rendered":"<p class=\"MsoNormal\"><font face=\"Calibri\"><font size=\"3\">Related posts:<\/font><\/font><\/p>\n<ul>\n<li>\n<div class=\"MsoNormal\"><font face=\"Calibri\"><font size=\"3\"><span lang=\"EN\"><\/p>\n<p><a href=\"https:\/\/blogs.msdn.com\/pfxteam\/archive\/2009\/10\/19\/9909371.aspx\"><font size=\"3\" face=\"Calibri\">What&#8217;s new in Beta 2 for the Task Parallel Library (1\/3)<\/font><\/a><\/p>\n<p><\/span><\/font><\/font><\/div>\n<\/li>\n<li>\n<div class=\"MsoNormal\"><a title=\"What's new in Beta 2 for the Task Parallel Library (3\/3)\" href=\"https:\/\/blogs.msdn.com\/pfxteam\/archive\/2009\/11\/04\/9917581.aspx\" target=\"_blank\"><font size=\"3\" face=\"Calibri\">What&#8217;s new in Beta 2 for the Task Parallel Library (3\/3)<\/font><\/a><\/div>\n<\/li>\n<\/ul>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Last week, we talked about how TPL adopted a new, better cancellation model.<span>&nbsp; <\/span>Today, we&rsquo;ll cover a change that makes <b>Tasks Detached by Default<\/b>, some <b>ContinueWhenAll\/Any Refactoring<\/b>, and the handy <b>UnobservedTaskException event<\/b>.<\/p>\n<p><\/font><\/font><\/p>\n<h2><font size=\"4\"><font color=\"#4f81bd\"><font face=\"Cambria\">Tasks are Detached by Default<\/p>\n<p><\/font><\/font><\/font><\/h2>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">In Beta 2, we have changed an important default.<span>&nbsp; <\/span>Tasks are now created as detached (instead of attached) if no options specify otherwise.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Let&rsquo;s consider the following code to review the difference between attached and detached Tasks.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span>Task p = Task.Factory.StartNew(() =&gt;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>Task c = Task.Factory.StartNew(() =&gt;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>DoWork();<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>});<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>});<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>p.Wait();<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">In Beta 1 and before, since the default options are used, &lsquo;c&rsquo; is created as a child Task of Task &lsquo;p&rsquo;, the parent Task; we refer to this as Task &lsquo;c&rsquo; being &ldquo;attached&rdquo; to Task &lsquo;p&rsquo;.<span>&nbsp; <\/span>This means that the p.Wait() statement will not return until the call to DoWork completes, because parent Tasks do not complete until all of their child Tasks complete.<span>&nbsp; <\/span>To opt out of this behavior, a user needs to create &lsquo;c&rsquo; with the DetachedFromParent option:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>Task c = Task.Factory.StartNew(() =&gt;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>DoWork();<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>}, <b>TaskCreationOptions.DetachedFromParent<\/b>);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><u>The original code shown behaves differently in Beta 2<\/u>.<span>&nbsp; <\/span>Now, by default, &lsquo;c&rsquo; is not related to &lsquo;p&rsquo; (it&rsquo;s &ldquo;detached&rdquo; by default), and the p.Wait() statement will return as soon as &lsquo;p&rsquo; completes, regardless of the status of Task &lsquo;c&rsquo; and thus regardless of when DoWork returns.<span>&nbsp; <\/span>To opt in to the parent\/child relationship, a user needs to create &lsquo;c&rsquo; with the AttachedToParent option:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>Task c = Task.Factory.StartNew(() =&gt;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>DoWork();<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>}, <b>TaskCreationOptions.AttachedToParent<\/b>);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\"><font size=\"3\">Here is a summary of the changes:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpFirst\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\"><font size=\"3\">Removed the DetachedFromParent option<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\"><font size=\"3\">Added the AttachedToParent option<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpLast\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\"><font size=\"3\">Changed the default behavior so that Tasks do not enlist in parent\/child relationships when no options are specified.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\"><font size=\"3\">There were a number of reasons why we decided that detached is the correct default and to move forward with this change, including:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpFirst\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\"><font size=\"3\"><b>Many users were using attached Tasks unknowingly.<span>&nbsp; <\/span><\/b>The vast majority of the time, users create Tasks for simple, asynchronous work.<span>&nbsp; <\/span>In such scenarios, parent\/child relationships (and the implicit waiting) are not needed.<span>&nbsp; <\/span>We found through many interactions that folks were just going with the default options and were accidentally opting in to this behavior.<span>&nbsp; <\/span>In the best case, this would only result in a slight performance cost. <span>&nbsp;<\/span>In the worst case, this would bring with it incorrect behavior that would lead to difficult to diagnose errors.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\"><font size=\"3\"><b>Easier migration from ThreadPool.QueueUserWorkItem<\/b>.<span>&nbsp; <\/span>Tasks are now the recommended way to queue work to the ThreadPool, but the easiest way to create Tasks resulted in different behavior from QueueUserWorkItem (where there&rsquo;s no concept of parent\/child work items).<span>&nbsp; <\/span>This change makes Task.Factory.StartNew (with no options) a true replacement for QueueUserWorkItem.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpLast\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\"><font face=\"Calibri\"><b>Additional behavior should be opt-in and pay-for-play<\/b>.<span>&nbsp; <\/span>Almost everything in TPL that results in additional behavior is opt-in, e.g. cancellation, LongRunning, PreferFairness.<span>&nbsp; <\/span>With the Beta 1 default, users opt-out of parent\/child relationships.<span>&nbsp; <\/span>In Beta 2, users opt-in, making it consistent.<span>&nbsp; <\/span>This makes the extra functionality provided by parent\/child relationships pay-for-play, such that you don&rsquo;t pay the cost for parents implicitly waiting for their children or for exceptions propagating from children to parents unless you need that functionality.<\/p>\n<p><\/font><\/font><\/p>\n<h1><font size=\"5\"><font color=\"#376092\"><font face=\"Cambria\">ContinueWhenAny\/All Refactoring<\/p>\n<p><\/font><\/font><\/font><\/h1>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">We have refactored the set of ContinueWhenAny and ContinueWhenAll overloads to make things more intuitive, consistent, and complete.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">To demonstrate the main issue, let&rsquo;s consider the following overload that was provided in Beta 1.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNoSpacing\"><span>public class TaskFactory&lt;TResult&gt;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>public Task&lt;TNewResult&gt; ContinueWhenAny(<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span>&nbsp;&nbsp;&nbsp; <span>&nbsp;&nbsp;&nbsp;&nbsp;<\/span>Task&lt;TResult&gt;[] tasks, <\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span>&nbsp;&nbsp;&nbsp; <span>&nbsp;&nbsp;&nbsp;&nbsp;<\/span>Func&lt;Task&lt;TResult&gt;, TNewResult&gt; continuationFunction);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span>}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">This confused the meaning of TaskFactory&lt;TResult&gt;, which is meant to create tasks of type Task&lt;TResult&gt;.<span>&nbsp; <\/span>However, with these overloads, TaskFactory&lt;TResult&gt; could be used to create tasks of type Task&lt;TNewResult&gt;. As an example, consider the code:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span>Task&lt;int&gt;[] taskOfInts = &#8230;;<br>Task&lt;<b>string<\/b>&gt; t = Task&lt;<b>int<\/b>&gt;.Factory.ContinueWhenAll(taskOfInts, _ =&gt; &ldquo;&rdquo;);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">This compiles and works just fine, but the type parameter mismatch (shown in bold) is certainly odd.<span>&nbsp; <\/span>To address this, we changed a bunch of overloads, so that instead of taking Task&lt;TResult&gt;s and returning a Task&lt;TNewResult&gt;, they take Task&lt;TAntecedentResult&gt;s and return Task&lt;TResult&gt;s.<span>&nbsp; <\/span>For example, the overload that replaced the above is:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"Code\"><span>public Task&lt;TResult&gt; ContinueWhenAny&lt;TAntecedentResult&gt;(<\/p>\n<p><\/span><\/p>\n<p class=\"Code\"><span>&nbsp;&nbsp;&nbsp; Task&lt;TAntecedentResult&gt;[] tasks,<\/p>\n<p><\/span><\/p>\n<p class=\"Code\"><span>&nbsp;&nbsp;&nbsp; Func&lt;Task&lt;TAntecedentResult&gt;, TResult&gt; continuationFunction);<\/p>\n<p><\/span><\/p>\n<p class=\"Code\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">And the above example becomes:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span>Task&lt;int&gt;[] taskOfInts = &#8230;;<br>Task&lt;string&gt; t = Task&lt;string&gt;.Factory.ContinueWhenAll(taskOfInts, _ =&gt; &ldquo;&rdquo;);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\"><font size=\"3\">In addition to this change, we also added, removed, or modified a number of other overloads to make the set consistent and complete.<span>&nbsp; <\/span>Now, the entire set of ContinueWhenAll and ContinueWhenAny overloads follow these clear rules:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpFirst\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Calibri\"><font size=\"3\">A TaskFactory creates Tasks, but also provides overloads to create Task&lt;TResult&gt;s.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpLast\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\"><font face=\"Calibri\">A TaskFactory&lt;TResult&gt; only ever creates Task&lt;TResult&gt;s (never Tasks or Task&lt;TNewResult&gt;s).<\/p>\n<p><\/font><\/font><\/p>\n<h1><font size=\"5\"><font color=\"#376092\"><font face=\"Cambria\">UnobservedTaskException event<\/p>\n<p><\/font><\/font><\/font><\/h1>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">We&rsquo;ve added an event that fires for every Task exception that goes unobserved.<span>&nbsp; <\/span>Recall that to &ldquo;observe&rdquo; a Task&rsquo;s exceptions, you must either Wait on the Task or access its Exception property after it has completed.<span>&nbsp; <\/span>At least one of these actions must be done before the Task object is garbage collected, or its exceptions will propagate (currently this occurs on the finalizer thread).<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">The new static event resides on the TaskScheduler class, and subscribing to it is straightforward.<span>&nbsp; <\/span>Here&rsquo;s an example to log all unobserved exceptions and mark them as observed (preventing them from being propagated).<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNoSpacing\"><span>TaskScheduler.UnobservedTaskException += <\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>(object sender, UnobservedTaskExceptionEventArgs exceptionArgs) =&gt;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>exceptionArgs.SetObserved();<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>LogException(exceptionArgs.Exception);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>};<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Some customers have complained that TPL&rsquo;s exception policy is too strict.<span>&nbsp; <\/span>The UnobservedTaskException event provides an easy way out by allowing you to simply squash all Task exceptions in an application (though using it in this manner is not recommended).<span>&nbsp; <\/span>The primary reason that we made the addition was to support host-plugin scenarios where a host application can still be perfectly useful in the presence of some truly harmless exceptions (thrown by buggy plugins).<span>&nbsp; <\/span>These scenarios may be achieved using the UnobservedTaskException event in conjunction with AppDomains to sandbox plugins.<span>&nbsp; <\/span>Look for a future post that describes this in more detail!<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">We&rsquo;re done for now!<span>&nbsp; <\/span>The 3<sup>rd<\/sup> and final post of this series will cover the new Unwrap APIs, a Parallel namespace change, and some changes under the covers.<\/p>\n<p><\/font><\/font><\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Related posts: What&#8217;s new in Beta 2 for the Task Parallel Library (1\/3) What&#8217;s new in Beta 2 for the Task Parallel Library (3\/3) Last week, we talked about how TPL adopted a new, better cancellation model.&nbsp; Today, we&rsquo;ll cover a change that makes Tasks Detached by Default, some ContinueWhenAll\/Any Refactoring, and the handy UnobservedTaskException [&hellip;]<\/p>\n","protected":false},"author":485,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[7908],"tags":[7907,7909,7912,7323],"class_list":["post-55968","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pfxteam","tag-net-4","tag-parallel-extensions","tag-task-parallel-library","tag-visual-studio-2010"],"acf":[],"blog_post_summary":"<p>Related posts: What&#8217;s new in Beta 2 for the Task Parallel Library (1\/3) What&#8217;s new in Beta 2 for the Task Parallel Library (3\/3) Last week, we talked about how TPL adopted a new, better cancellation model.&nbsp; Today, we&rsquo;ll cover a change that makes Tasks Detached by Default, some ContinueWhenAll\/Any Refactoring, and the handy UnobservedTaskException [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/55968","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\/485"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=55968"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/55968\/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=55968"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=55968"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=55968"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}