{"id":102463,"date":"2019-05-01T07:00:00","date_gmt":"2019-05-01T14:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102463"},"modified":"2019-06-06T17:47:12","modified_gmt":"2019-06-07T00:47:12","slug":"20190501-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190501-00\/?p=102463","title":{"rendered":"Async-Async: Consequences for parameters to parallel asynchronous calls"},"content":{"rendered":"<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190430-35\/?p=102460\"> Last time<\/a>, we learned about a feature known as Async-Async which makes asynchronous operations even more asynchronous by pretending that they started before they actually did. The introduction of Async-Async is intended to be transparent to both the client and the server, provided they were following the rules to begin with. Of course, if you weren&#8217;t following the rules, then you may notice some side effects.<\/p>\n<p>From the client side, it means that you cannot mutate the parameters of an asynchronous operation until the operation completes. This was never permitted to begin with, but people sometimes got away with it because they &#8220;knew&#8221; that certain parameters were consumed before the initial method returned an asynchronous operation.<\/p>\n<pre>\/\/ Code in italics is wrong.\r\n\r\n\/\/ Create three widgets in parallel.\r\nvar options = new WidgetOptions();\r\n\r\n\/\/ Create a blue widget.\r\noptions.Color = Colors.Blue;\r\nvar task1 = Widget.CreateAsync(options);\r\n\r\n\/\/ Create another blue widget.\r\nvar task2 = Widget.CreateAsync(options);\r\n\r\n<i>\/\/ Create a red widget.\r\noptions.Color = Colors.Red;\r\nvar task3 = Widget.CreateAsync(options);<\/i>\r\n\r\n\/\/ Wait for all the widgets to be created.\r\nawait Task.WhenAll(task1, task2, task3);\r\n\r\n\/\/ Get the widgets.\r\nvar widget1 = task1.Result;\r\nvar widget2 = task2.Result;\r\nvar widget3 = task3.Result;\r\n<\/pre>\n<p>This code &#8220;knows&#8221; that the <code>Widget.<\/code><code>Create\u00adAsync<\/code> method looks at the <code>options.Color<\/code> <i>before<\/i> it returns with an <code>IAsync\u00adOperation<\/code>. It therefore &#8220;knows&#8221; that any changes to the <code>options<\/code> after <code>Widget.<\/code><code>Create\u00adAsync<\/code> returns will not have any effect on the widget being created, so it goes ahead and reconfigures the <code>options<\/code> object so it can be used for the third widget.<\/p>\n<p>This code does not work when Async-Async is enabled. The calls to <code>Widget.<\/code><code>Create\u00adAsync<\/code> will return immediately with fake <code>IAsync\u00adOperation<\/code>s, while the real calls to <code>Widget.<\/code><code>Create\u00adAsync<\/code> are still in progress. As we saw earlier, the result of the real call to <code>Widget.<\/code><code>Create\u00adAsync<\/code> will be connected to the fake <code>IAsync\u00adOperation<\/code> so that you get the result you want, but the timing has changed to improve performance. If the above code manages to change the <code>options.<\/code><code>Color<\/code> to red before one of the first two real calls to <code>Widget.<\/code><code>Create\u00adAsync<\/code> reads the options, then one or both of the first two widgets will end up red rather than blue.<\/p>\n<p>This is basically a case of violating one of the <a href=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20060320-13\/?p=31853\"> basic ground rules for programming<\/a>: You cannot change a parameter while the function call is in progress. It&#8217;s just that for asynchronous operations, the &#8220;in progress&#8221; extends all the way through to the completion of the asynchronous operation.<\/p>\n<p>It&#8217;s fine to kick off multiple asynchronous operations. Just make sure they don&#8217;t interfere with each other.<\/p>\n<pre>\/\/ Create three widgets in parallel.\r\nvar options = new WidgetOptions();\r\n\r\n\/\/ Create a blue widget.\r\noptions.Color = Colors.Blue;\r\nvar task1 = Widget.CreateAsync(options);\r\n\r\n\/\/ Create another blue widget.\r\nvar task2 = Widget.CreateAsync(options);\r\n\r\n\/\/ Create a red widget.\r\n<span style=\"color: blue;\">options = new WidgetOptions();<\/span>\r\noptions.Color = Colors.Red;\r\nvar task3 = Widget.CreateAsync(options);\r\n\r\n\/\/ Wait for all the widgets to be created.\r\nawait Task.WhenAll(task1, task2, task3);\r\n\r\n\/\/ Get the widgets.\r\nvar widget1 = task1.Result;\r\nvar widget2 = task2.Result;\r\nvar widget3 = task3.Result;\r\n<\/pre>\n<p>This time, we create a new <code>Widget\u00adOptions<\/code> object for the final call to <code>Widget.\u00adCreate\u00adAsync<\/code>. That way, each call to <code>Widget.<\/code><code>Create\u00adAsync<\/code> gets an <code>options<\/code> object that is stable for the duration of the call. It&#8217;s okay to share the <code>options<\/code> object among multiple calls (like we did for the first two blue widgets), but don&#8217;t change them while there is still an asynchronous operation that is using them.<\/p>\n<p>Of course, once the operation completes, then you are welcome to do whatever you like to the <code>options<\/code>, since the operation isn&#8217;t using them any more.<\/p>\n<pre>\/\/ Create three widgets in series.\r\nvar options = new WidgetOptions();\r\n\r\n\/\/ Create a blue widget.\r\noptions.Color = Colors.Blue;\r\nvar widget1 = await Widget.CreateAsync(options);\r\n\r\n\/\/ Create another blue widget.\r\nvar widget2 = await Widget.CreateAsync(options);\r\n\r\n\/\/ Create a red widget.\r\noptions.Color = Colors.Red;\r\nvar widget3 = await Widget.CreateAsync(options);\r\n<\/pre>\n<p>In this case, we created the widgets in series. We changed the <code>options<\/code> after awaiting the result of the operation, so we know that the operation is finished and it is safe to modify the <code>options<\/code> for a new call.<\/p>\n<p>Next time, we&#8217;ll look at another consequence of Async-Async.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Keep those parameter stable.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[26],"class_list":["post-102463","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>Keep those parameter stable.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102463","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=102463"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102463\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=102463"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102463"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102463"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}