{"id":55970,"date":"2009-10-19T12:42:00","date_gmt":"2009-10-19T12:42:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/pfxteam\/2009\/10\/19\/whats-new-in-beta-2-for-the-task-parallel-library-part-13\/"},"modified":"2009-10-19T12:42:00","modified_gmt":"2009-10-19T12:42:00","slug":"whats-new-in-beta-2-for-the-task-parallel-library-part-13","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/whats-new-in-beta-2-for-the-task-parallel-library-part-13\/","title":{"rendered":"What\u2019s new in Beta 2 for the Task Parallel Library? (Part 1\/3)"},"content":{"rendered":"<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Related&nbsp;posts:<\/font><\/font><\/p>\n<ul>\n<li>\n<div class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\"><a title=\"What's new in Beta 2 for the Task Parallel Library? (Part 2\/3)\" href=\"https:\/\/blogs.msdn.com\/pfxteam\/archive\/2009\/10\/27\/9913610.aspx\">What&#8217;s new in Beta 2 for the Task Parallel Library? (Part 2\/3)<\/a><\/font>&nbsp;<\/div>\n<\/li>\n<li>\n<div class=\"MsoNormal\">\n<div class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\"><a title=\"What's new in Beta 2 for the Task Parallel Library? (Part 3\/3)\" href=\"https:\/\/blogs.msdn.com\/pfxteam\/archive\/2009\/11\/04\/9917581.aspx\" target=\"_blank\">What&#8217;s new in Beta 2 for the Task Parallel Library? (Part 3\/3)<\/a><\/font>&nbsp;<\/div>\n<\/div>\n<\/li>\n<\/ul>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Visual Studio 2010 and .NET 4 Beta 2 is&nbsp;here!<span>&nbsp; <\/span>In terms of completeness and readiness for production coding, Beta 2 promises to be much better than Beta 1, and TPL is one component that delivers significant improvements over what was previously available.<span>&nbsp; <\/span>To get you excited about it, this series of posts details key additions and changes for Beta 2.<span>&nbsp; <\/span>Enjoy!<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">In this post, we&rsquo;re talking about <b>cancellation<\/b>.<span>&nbsp; <\/span>A few months ago, our flurry of &ldquo;What&rsquo;s new in Beta 1&rdquo; posts hinted at a new cancellation model and promised more information about it.<span>&nbsp; <\/span>We made good on that promise with <\/font><a href=\"https:\/\/blogs.msdn.com\/pfxteam\/archive\/2009\/05\/22\/9635790.aspx\"><font color=\"#0000ff\" size=\"3\" face=\"Calibri\">.NET 4 Cancellation Framework<\/font><\/a><font size=\"3\"><font face=\"Calibri\">, and this post goes further to explain how TPL, specifically, has fully adopted the new model.<\/p>\n<p><\/font><\/font><\/p>\n<h2><font size=\"4\"><font color=\"#4f81bd\"><font face=\"Cambria\">The Old Way (Beta 1 and before)<\/p>\n<p><\/font><\/font><\/font><\/h2>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Let&rsquo;s consider the following code to review the old TPL cancellation model.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span>Task myTask = 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>for (; ; )<\/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>if (Task.Current.IsCancellationRequested)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>Task.Current.AcknowledgeCancellation();<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>return;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>&#8230;<\/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>\/\/ Elsewhere.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>myTask.Cancel();<\/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\">myTask just loops infinitely, checking its IsCancellationRequested property to see if it has been canceled.<span>&nbsp; <\/span>Elsewhere, cancellation is requested on myTask using the Cancel method.<span>&nbsp; <\/span>At that point, myTask agrees to get canceled by calling its AcknowledgeCancellation method and returning.<span>&nbsp; <\/span>This is all necessary, because Task cancellation is cooperative; <span>&nbsp;<\/span>to enter the Canceled state, outside logic must request cancellation <u>and<\/u> the Task must acknowledge that cancellation request.<span>&nbsp; <\/span>However, note that cooperative cancellation is only relevant for already running Tasks.<span>&nbsp; <\/span>If a Task&rsquo;s Cancel method is called before it is in the Running state, it will transition directly into the Canceled state.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Waiting on a canceled Task results in a TaskCanceledException wrapped in an AggregateException, hence the try\/catch block.<span>&nbsp; <\/span>Executing this code prints &ldquo;Canceled&rdquo; to the console, indicating that myTask was successfully canceled.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">This approach works, but we identified a number of problems with it including the following:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpFirst\"><span><span><font size=\"3\" face=\"Calibri\">1.<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\"><font face=\"Calibri\">The cancellation model required exposing a Task.Current static property.<span>&nbsp; <\/span>This leaks implementation details from libraries that utilize Tasks internally; any code called from the Task can muck with the current Task or take dependencies on its existence.<span>&nbsp; <\/span>Imagine calling into 3<sup>rd<\/sup> party code and having that code cancel your Tasks, schedule continuations off of them, etc.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font size=\"3\" face=\"Calibri\">2.<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\"><font face=\"Calibri\">Anyone with a Task&rsquo;s reference can request cancellation on it.<span>&nbsp; <\/span>In many scenarios, it is valuable to separate the ability to check for cancellation and the ability to actually request cancellation.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font size=\"3\" face=\"Calibri\">3.<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\"><font face=\"Calibri\">From a cancellation perspective, Tasks didn&rsquo;t compose well with other APIs that were cancelable, such as executing a cancelable PLINQ query inside of a Task.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpLast\"><span><span><font size=\"3\" face=\"Calibri\">4.<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\"><font face=\"Calibri\">Tasks were often more expensive than they needed to be, due to needing to track extra cancellation state per Task.<\/p>\n<p><\/font><\/font><\/p>\n<h2><font size=\"4\"><font color=\"#4f81bd\"><font face=\"Cambria\">The New Way (Beta 2 and beyond)<\/p>\n<p><\/font><\/font><\/font><\/h2>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">The new TPL cancellation model is centered around two types: CancellationTokenSource and CancellationToken.<span>&nbsp; <\/span>You can read up about them in the post linked to above, but here is a simplified overview of their APIs:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span>namespace System.Threading<br>{<br><span>&nbsp;&nbsp;&nbsp; <\/span>public sealed class CancellationTokenSource<br><span>&nbsp;&nbsp;&nbsp; <\/span>{<br><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>public void Cancel();<br><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>public CancellationToken Token { get; }<br><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>&#8230;<br><span>&nbsp;&nbsp;&nbsp; <\/span>}<\/p>\n<p><span>&nbsp;&nbsp;&nbsp; <\/span>public struct CancellationToken<br><span>&nbsp;&nbsp;&nbsp; <\/span>{<br><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>public Boolean IsCancellationRequested { get; }<br><span>&nbsp;&nbsp; <\/span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span>public void ThrowIfCancellationRequested();<br><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>&#8230;<br><span>&nbsp;&nbsp;&nbsp; <\/span>}<br>}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\"><font size=\"3\">Here&rsquo;s the gist.<span>&nbsp; <\/span>A CancellationTokenSource contains a CancellationToken, and it can request cancellation on that token using a Cancel method.<span>&nbsp; <\/span>A CancellationToken can only check if cancellation has been requested on it.<span>&nbsp; <\/span>Ignore the ThrowIfCancellationRequested method for now; we&rsquo;ll see why it&rsquo;s handy later.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\"><font size=\"3\">Adopting the new model involved not only adding support for these two types, but also ripping out the old model.<span>&nbsp; <\/span>Here&rsquo;s 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\">All cancellation-related APIs on the Task class were removed (Cancel, AcknowledgeCancellation, IsCancellationRequested, etc)<\/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\">Other APIs that were no longer relevant were removed (Task.Current, Task.Parent, TaskCreationOptions.RespectParentCancellation, etc)<\/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\">Overloads that accept CancellationToken were added to many methods (StartNew, ContinueWith, etc)<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">And now, here&rsquo;s a table that outlines how achieving cancellation in TPL has changed:<\/p>\n<p><\/font><\/font><\/p>\n<table class=\"LightShading1\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"213\">\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\">Action<\/p>\n<p><\/font><\/span><\/b><\/p>\n<\/td>\n<td valign=\"top\" width=\"213\">\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\">Old Model<\/p>\n<p><\/font><\/span><\/b><\/p>\n<\/td>\n<td valign=\"top\" width=\"213\">\n<p class=\"MsoNormal\"><b><span><font face=\"Calibri\">New Model<\/p>\n<p><\/font><\/span><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"213\"><font face=\"Calibri\"><\/font><\/td>\n<td valign=\"top\" width=\"213\"><font face=\"Calibri\"><\/font><\/td>\n<td valign=\"top\" width=\"213\"><font face=\"Calibri\"><\/font><\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"213\">\n<p class=\"MsoNormal\"><span><font face=\"Calibri\">To set up cancellation<\/p>\n<p><\/font><\/span><\/p>\n<\/td>\n<td valign=\"top\" width=\"213\">\n<p class=\"MsoNormal\"><span><font face=\"Calibri\">Just create a Task<\/p>\n<p><\/font><\/span><\/p>\n<\/td>\n<td valign=\"top\" width=\"213\">\n<p class=\"MsoNormal\"><span><font face=\"Calibri\">Create a CancellationTokenSource and pass its Token to an API that creates a Task.<\/p>\n<p><\/font><\/span><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"213\">\n<p class=\"MsoNormal\"><span><font face=\"Calibri\">To check if cancellation has been requested<\/p>\n<p><\/font><\/span><\/p>\n<\/td>\n<td valign=\"top\" width=\"213\">\n<p class=\"MsoNormal\"><span><font face=\"Calibri\">Check the IsCancellationRequested property on the relevant Task<\/p>\n<p><\/font><\/span><\/p>\n<\/td>\n<td valign=\"top\" width=\"213\">\n<p class=\"MsoNormal\"><span><font face=\"Calibri\">Check the IsCancellationRequested property on the CancellationToken that was passed to the API that created the Task<\/p>\n<p><\/font><\/span><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"213\">\n<p class=\"MsoNormal\"><span><font face=\"Calibri\">To acknowledge cancellation<\/p>\n<p><\/font><\/span><\/p>\n<\/td>\n<td valign=\"top\" width=\"213\">\n<p class=\"MsoNormal\"><span><font face=\"Calibri\">Check to ensure that IsCancellationRequested is true, then call the AcknowledgeCancellation() method on the relevant Task<\/p>\n<p><\/font><\/span><\/p>\n<\/td>\n<td valign=\"top\" width=\"213\">\n<p class=\"MsoNormal\"><span><font face=\"Calibri\">Throw an OperationCanceledException with the task&rsquo;s CancellationToken<\/p>\n<p><\/font><\/span><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"213\">\n<p class=\"MsoNormal\"><span><font face=\"Calibri\">To cancel a tree of tasks<\/p>\n<p><\/font><\/span><\/p>\n<\/td>\n<td valign=\"top\" width=\"213\">\n<p class=\"MsoNormal\"><span><font face=\"Calibri\">Create all of the tasks as attached tasks and with the RespectParentCancellation flag set, then cancel the root task.<\/p>\n<p><\/font><\/span><\/p>\n<\/td>\n<td valign=\"top\" width=\"213\">\n<p class=\"MsoNormal\"><span><font face=\"Calibri\">Pass the same CancellationToken to all Tasks, then cancel the associated CancellationTokenSource<\/p>\n<p><\/font><\/span><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">\n<p><font size=\"3\" face=\"Calibri\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">To see how this all works, let&rsquo;s rewrite the code above for Beta 2.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span>CancellationTokenSource cts = new CancellationTokenSource();<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>CancellationToken token = cts.Token;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>Task myTask = 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>for (; ; )<\/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>token.ThrowIfCancellationRequested();<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>&#8230;<\/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>}, token);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>\/\/ Elsewhere.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>cts.Cancel();<\/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\">A CancellationTokenSource (cts) is initialized, and a CancellationToken (token) is initialized to cts&rsquo;s Token.<span>&nbsp; <\/span>myTask still loops indefinitely, but now it calls ThrowIfCancellationRequested.<span>&nbsp; <\/span>This method just checks the IsCancellationRequested property on a CancellationToken.<span>&nbsp; <\/span>If true, it throws<span>&nbsp; <\/span>an OperationCanceledException(token), which is the way to acknowledge cancellation in the new model.<span>&nbsp; <\/span>Elsewhere, Cancel is called on cts to request cancellation on myTask.<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">The new model addresses all of the problems with the old model listed above:<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpFirst\"><span><span><font size=\"3\" face=\"Calibri\">1.<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\"><font face=\"Calibri\">No more Task.Current (Task.Parent was removed too, as it could be used to get at the current Task).<\/p>\n<p><\/font><\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font size=\"3\" face=\"Calibri\">2.<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\"><font face=\"Calibri\">The ability to check for cancellation requests may now be separated from the ability to request cancellation.<span>&nbsp; <\/span>If only the <\/font><\/font><\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Related&nbsp;posts: What&#8217;s new in Beta 2 for the Task Parallel Library? (Part 2\/3)&nbsp; What&#8217;s new in Beta 2 for the Task Parallel Library? (Part 3\/3)&nbsp; Visual Studio 2010 and .NET 4 Beta 2 is&nbsp;here!&nbsp; In terms of completeness and readiness for production coding, Beta 2 promises to be much better than Beta 1, and TPL [&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,7915,7909,7912],"class_list":["post-55970","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pfxteam","tag-net-4","tag-cancellation","tag-parallel-extensions","tag-task-parallel-library"],"acf":[],"blog_post_summary":"<p>Related&nbsp;posts: What&#8217;s new in Beta 2 for the Task Parallel Library? (Part 2\/3)&nbsp; What&#8217;s new in Beta 2 for the Task Parallel Library? (Part 3\/3)&nbsp; Visual Studio 2010 and .NET 4 Beta 2 is&nbsp;here!&nbsp; In terms of completeness and readiness for production coding, Beta 2 promises to be much better than Beta 1, and TPL [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/55970","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=55970"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/55970\/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=55970"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=55970"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=55970"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}