{"id":55931,"date":"2010-04-05T10:00:00","date_gmt":"2010-04-05T10:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/pfxteam\/2010\/04\/05\/parallelextensionsextras-tour-3-additional-task-extensions-methods\/"},"modified":"2010-04-05T10:00:00","modified_gmt":"2010-04-05T10:00:00","slug":"parallelextensionsextras-tour-3-additional-task-extensions-methods","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/parallelextensionsextras-tour-3-additional-task-extensions-methods\/","title":{"rendered":"ParallelExtensionsExtras Tour &#8211; #3 &#8211; Additional Task Extensions Methods"},"content":{"rendered":"<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\"><em>(The full set of ParallelExtensionsExtras Tour posts is available&nbsp;<\/em><a href=\"https:\/\/blogs.msdn.com\/pfxteam\/archive\/2010\/04\/04\/9990342.aspx\"><font color=\"#dd4a21\"><em>here<\/em><\/font><\/a><em>.)<\/em>&nbsp;&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">In our <a href=\"https:\/\/blogs.msdn.com\/pfxteam\/archive\/2010\/04\/04\/9990349.aspx\">last ParallelExtensionsExtras tour post<\/a>, we discussed implementing an extension ToObservable method for Task&lt;TResult&gt;.<span>&nbsp; <\/span>This is just one of a myriad of extra pieces of functionality that are useful with Tasks, and the TaskExtrasExtensions.cs file in <\/font><a href=\"https:\/\/code.msdn.microsoft.com\/ParExtSamples\"><font size=\"3\" face=\"Calibri\">ParallelExtensionsExtras<\/font><\/a><font size=\"3\"><font face=\"Calibri\"> includes several.<span>&nbsp; <\/span>In this post, we&rsquo;ll cover a few of the more interesting ones.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><b><font size=\"3\"><font face=\"Calibri\">WithTimeout<\/p>\n<p><\/font><\/font><\/b><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">When dealing with operations that run asynchronously, it&rsquo;s not uncommon to want to incorporate a timeout, such that other code that depends on the asynchronous operation isn&rsquo;t forced to wait too long.<span>&nbsp; <\/span>We can tackle this use case with a simple WithTimeout method:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\">public static Task&lt;TResult&gt; WithTimeout&lt;TResult&gt;(<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>this Task&lt;TResult&gt; task, TimeSpan timeout)<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\">{<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>var result = new TaskCompletionSource&lt;TResult&gt;(task.AsyncState);<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>var timer = new Timer(_ =&gt; result.TrySetCanceled(), <\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>null, timeout, TimeSpan.FromMilliseconds(-1));<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>task.ContinueWith(t =&gt;<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>timer.Dispose();<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>result.TrySetFromTask(t);<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>});<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>return result.Task;<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\">}<\/p>\n<p><\/font><\/p>\n<p class=\"Code\">\n<p><font face=\"Consolas\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">This method works by creating a <\/font><a href=\"https:\/\/blogs.msdn.com\/pfxteam\/archive\/2009\/06\/02\/9685804.aspx\"><font size=\"3\" face=\"Calibri\">TaskCompletionSource<\/font><\/a><font size=\"3\"><font face=\"Calibri\"> to serve as the representation for the potentially timed-out task.<span>&nbsp; <\/span>A System.Threading.Timer instance is also created to fire once after the user specified period of time.<span>&nbsp; <\/span>When the timer does fire, it attempts to transition the TaskCompletionSource into a canceled state, which will only take effect if the TaskCompletionSource hasn&rsquo;t already been completed.<span>&nbsp; <\/span>In addition, a continuation is used to dispose of the timer and to transition the TaskCompletionSource to a completed state when the original task finishes.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">TrySetFromTask is another utility extension method defined in ParallelExtensionsExtras that serves to wrap up some frequently needed logic when using a TaskCompletionSource to serve as a proxy for another Task&lt;TResult&gt;:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\">public static bool TrySetFromTask&lt;TResult&gt;(<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>this TaskCompletionSource&lt;TResult&gt; resultSetter, Task&lt;TResult&gt; task)<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\">{<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>switch (task.Status)<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>case TaskStatus.RanToCompletion: <\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>return resultSetter.TrySetResult(task.Result);<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>case TaskStatus.Faulted: <\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>return resultSetter.TrySetException(<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>task.Exception.InnerExceptions);<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>case TaskStatus.Canceled: <\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>return resultSetter.TrySetCanceled();<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>default: <\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>throw new InvalidOperationException(&#8220;The task was not completed.&#8221;);<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>}<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\">}<\/p>\n<p><\/font><\/p>\n<p class=\"Code\">\n<p><font face=\"Consolas\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">TrySetFromTask is defined in TaskCompletionSourceExtensions.cs<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><b><font size=\"3\"><font face=\"Calibri\">WithAsyncCallback<\/p>\n<p><\/font><\/font><\/b><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Tasks implement IAsyncResult, which make them useful&nbsp;in custom implementations of the Asnchronous Programming Model (APM) pattern.<span>&nbsp; <\/span>However, when implementing the APM pattern, there are some very important rules that need to be followed.<span>&nbsp; <\/span>For example, the object state parameter passed to the BeginXx method must be returned from the resulting IAsyncResult&rsquo;s AsyncState property, the AsyncCallback passed to BeginXx must be invoked when the asynchronous operation completes, and it must be invoked with the exact same IAsyncResult returned from the BeginXx method as its argument.<span>&nbsp; <\/span>We can implement a WithAsyncCallback method to accomplish all of this:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\">public static Task WithAsyncCallback(<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>this Task task, AsyncCallback callback, object state)<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\">{<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>var tcs = new TaskCompletionSource&lt;object&gt;(state);<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>task.ContinueWith(_ =&gt;<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>tcs.TrySetFromTask(task);<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>if (callback != null) callback(tcs.Task);<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>});<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\"><span>&nbsp;&nbsp;&nbsp; <\/span>return tcs.Task;<\/p>\n<p><\/font><\/p>\n<p class=\"Code\"><font face=\"Consolas\">}<\/p>\n<p><\/font><\/p>\n<p class=\"Code\">\n<p><font face=\"Consolas\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">As with WithTimeout, a TaskCompletionSource instance is used, here to represent the IAsyncResult that should be both returned from BeginXx and passed to the AsyncCallback.<span>&nbsp; <\/span>When the source task completes, using ContinueWith we transfer the task&rsquo;s results to the TaskCompletionSource and we then invoke the AsyncCallback.<\/font><\/font><\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>(The full set of ParallelExtensionsExtras Tour posts is available&nbsp;here.)&nbsp;&nbsp; In our last ParallelExtensionsExtras tour post, we discussed implementing an extension ToObservable method for Task&lt;TResult&gt;.&nbsp; This is just one of a myriad of extra pieces of functionality that are useful with Tasks, and the TaskExtrasExtensions.cs file in ParallelExtensionsExtras includes several.&nbsp; In this post, we&rsquo;ll cover a [&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":[7907,7911,7909,7924,7912],"class_list":["post-55931","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pfxteam","tag-net-4","tag-code-samples","tag-parallel-extensions","tag-parallelextensionsextras","tag-task-parallel-library"],"acf":[],"blog_post_summary":"<p>(The full set of ParallelExtensionsExtras Tour posts is available&nbsp;here.)&nbsp;&nbsp; In our last ParallelExtensionsExtras tour post, we discussed implementing an extension ToObservable method for Task&lt;TResult&gt;.&nbsp; This is just one of a myriad of extra pieces of functionality that are useful with Tasks, and the TaskExtrasExtensions.cs file in ParallelExtensionsExtras includes several.&nbsp; In this post, we&rsquo;ll cover a [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/55931","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=55931"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/55931\/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=55931"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=55931"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=55931"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}