{"id":543,"date":"2011-05-10T09:00:00","date_gmt":"2011-05-10T09:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/vbteam\/2011\/05\/10\/simultaneous-async-tasks-alan-berman\/"},"modified":"2024-07-05T12:39:48","modified_gmt":"2024-07-05T19:39:48","slug":"simultaneous-async-tasks-alan-berman","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/vbteam\/simultaneous-async-tasks-alan-berman\/","title":{"rendered":"Simultaneous Async Tasks (Alan Berman)"},"content":{"rendered":"<p>The new Async feature in the <a href=\"http:\/\/msdn.microsoft.com\/en-US\/vstudio\/async\">Visual Studio Async CTP (SP1 Refresh)<\/a> provides an elegantly simple technique to make code asynchronous.<\/p>\n<p>Our writing team uses an internal app that would benefit from asynchronous calls.&nbsp; For each URL contained in the MSDN documentation that we publish, the app lists the title from the link, and the title parsed from HTML in the downloaded web page.&nbsp; We use the app to verify that URL links are valid.<\/p>\n<p>The following example is a very simplified version of the relevant code, which does synchronous reads of multiple web pages.<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">Imports System.Net <br \/>Imports System.Threading.Tasks<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">Module Module1 <br \/>&nbsp;&nbsp;&nbsp; Sub Main() <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim urls As List(Of String) = BuildURLs()<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim startTime = Date.Now<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetWebPagesSynchronous(urls) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8216;GetWebPagesAsync(urls).Wait()<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim seconds = (Date.Now &#8211; startTime).TotalSeconds<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine() <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine(&#8220;Ended in &#8221; &amp; seconds &amp; &#8221; seconds&#8221;) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.ReadKey() <br \/>&nbsp;&nbsp;&nbsp; End Sub<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp; Private Sub GetWebPagesSynchronous(urls As List(Of String)) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim client As New WebClient<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; For Each url In urls <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim text = client.DownloadString(New Uri(url)) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine(GetTitle(text)) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Next <br \/>&nbsp;&nbsp;&nbsp; End Sub<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp; Private Function GetTitle(input As String) As String <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Const startText = &#8220;&lt;title&gt;&#8221; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Const endText = &#8220;&lt;\/title&gt;&#8221;<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim startIndex = input.IndexOf(startText) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If startIndex = -1 Then <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Return &#8220;not found&#8221; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Else <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; startIndex += startText.Length <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim endIndex = input.IndexOf(endText, startIndex) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim result = input.Substring(startIndex, endIndex &#8211; startIndex) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Return result.Trim() <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End If <br \/>&nbsp;&nbsp;&nbsp; End Function<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp; Private Function BuildURLs() As List(Of String) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Return New List(Of String) From <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;<\/span><a href=\"http:\/\/www.microsoft.com%22\"><span style=\"font-family: Consolas;font-size: small\">http:\/\/www.microsoft.com&#8221;<\/span><\/a><span style=\"font-family: Consolas;font-size: small\">, <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;<\/span><a href=\"http:\/\/msdn.com%22\"><span style=\"font-family: Consolas;font-size: small\">http:\/\/msdn.com&#8221;<\/span><\/a> <br \/><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br \/>&nbsp;&nbsp;&nbsp; End Function <br \/>End Module <br \/><\/span><\/p>\n<p>&nbsp;<\/p>\n<p>The following method transforms the above example to use the Async feature.&nbsp; Starting with the above example, change Main to call GetWebPagesAsync(urls).Wait() instead of GetWebPagesSynchronous(urls).<\/p>\n<p>In your project, add a reference to AsyncCtpLibrary.dll, which is in My DocumentsMicrosoft Visual Studio Async CTPSamples.<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">Private Async Function GetWebPagesAsync(urls As List(Of String)) As Task<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp; Dim theTasks As New List(Of Task(Of String))<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp; For Each url In urls <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim client As New WebClient()<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim theTask As Task(Of String) = <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; client.DownloadStringTaskAsync(New Uri(url))<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; theTasks.Add(theTask) <br \/>&nbsp;&nbsp;&nbsp; Next<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp; &#8216; Wait until the tasks are done.<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp; &#8216; The Await statement causes execution to immediately return <br \/>&nbsp;&nbsp;&nbsp; &#8216; to the calling method, returning a new task. When all of the <br \/>&nbsp;&nbsp;&nbsp; &#8216; tasks complete, execution continues within this method.<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp; &#8216; TaskEx.WhenAll would normally be Task.WhenAll. <br \/>&nbsp;&nbsp;&nbsp; &#8216; It&#8217;s TaskEx.WhenAll in the CTP only. <br \/>&nbsp;&nbsp;&nbsp; Await TaskEx.WhenAll(theTasks)<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp; For Each theTask In theTasks <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine(GetTitle(theTask.Result)) <br \/>&nbsp;&nbsp;&nbsp; Next <br \/>End Function<\/span><\/p>\n<p>&nbsp;<\/p>\n<p>For each URL in the list, the DownloadStringTaskAsync method returns a Task object that is then stored in a generic List.&nbsp; The code waits for completion of all of the tasks by using an Await statement and the Task.WhenAll method. The WhenAll method accepts an object that implements <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/9eekhta0.aspx\">IEnumerable(T)<\/a>, including <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/6sh2ey19.aspx\">List(T)<\/a>.<\/p>\n<p>The code spins up all of these tasks without worrying about running out of resources from a language perspective.&nbsp; I was inclined to write code to carefully throttle the calls to conserve resources, but was told that is unnecessary.<\/p>\n<p>On computer running Window\ns 7, at a randomly convenient time, I ran the above console apps five times synchronously and five times asynchronously, with 20 URLs and 100 URLs. For 20 URLs, the average time to complete was 7.2 seconds for synchronous and 3.2 seconds for Async.&nbsp; For 100 URLs, the average was 39.5 seconds for synchronous, and 28.5 seconds for Async.&nbsp; With 100 URLs on another day, the average was 39.9 seconds for synchronous, and 13.5 seconds for Async.&nbsp; The results are specific to the conditions, and your results will vary.<\/p>\n<p>Note that the speedup in the Async example is almost entirely from the parallel processing, not the asynchronous processing.&nbsp; The advantages of asynchrony are that it does not tie up multiple threads, and that it does not tie up the user interface thread.<\/p>\n<p><strong>Cancellation<\/strong><\/p>\n<p>The following example adds <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd997364.aspx\">cancellation<\/a>.&nbsp; To actually cancel the operation, modify the code to change cancelIt = False to cancelIt = True, and change the Thread.Sleep call to specify the milliseconds before cancellation.<\/p>\n<p>This code creates a <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.threading.cancellationtokensource.aspx\">CancellationTokenSource<\/a> that contains a <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.threading.cancellationtoken.aspx\">CancellationToken<\/a>. The same cancellation token is passed to every call to DownloadStringTaskAsync. The CancellationTokenSource is also used to invoke cancellation.<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">Imports System.Net <br \/>Imports System.Threading <br \/>Imports System.Threading.Tasks<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">Module Module1 <br \/>&nbsp;&nbsp;&nbsp; Sub Main() <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim urls As List(Of String) = BuildURLs()<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim startTime = Date.Now<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ProcessAsyncCancellable(urls)<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim seconds = (Date.Now &#8211; startTime).TotalSeconds<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine() <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine(&#8220;Ended in &#8221; &amp; seconds &amp; &#8221; seconds&#8221;) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.ReadKey() <br \/>&nbsp;&nbsp;&nbsp; End Sub<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp; Private Sub ProcessAsyncCancellable(urls As List(Of String)) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim cts As New CancellationTokenSource()<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cts.Token.Register(Sub() Console.WriteLine(&#8220;cancelling&#8221;))<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim theTask As Task = GetWebPagesAsync(urls, cts)<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8216; To cancel midstream, set cancelIt to True. <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim cancelIt = False <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If cancelIt Then <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8216; Set the milliseconds before cancelling. <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.Sleep(2000) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cts.Cancel() <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Else <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; theTask.Wait() <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End If <br \/>&nbsp;&nbsp;&nbsp; End Sub<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp; Private Async Function GetWebPagesAsync( _ <br \/>&nbsp;&nbsp;&nbsp; urls As List(Of String), <br \/>&nbsp;&nbsp;&nbsp; cts As CancellationTokenSource) As Task<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim theTasks As New List(Of Task(Of String))<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; For Each webAddress In urls <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim client As New WebClient()<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim theTask As Task(Of String) = <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; client.DownloadStringTaskAsync(New Uri(webAddress), cts.Token)<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; theTasks.Add(theTask) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Next<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Await TaskEx.WhenAll(theTasks)<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; For Each theTask In theTasks <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine(GetTitle(theTask.Result)) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Next <br \/>&nbsp;&nbsp;&nbsp; End Function<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp; Private Function BuildURLs() As List(Of String) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Return New List(Of String) From <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;<\/span><a href=\"http:\/\/www.microsoft.com%22\"><span style=\"font-family: Consolas;font-size: small\">http:\/\/www.microsoft.com&#8221;<\/span><\/a><span style=\"font-family: Consolas;font-size: small\">, <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;<\/span><a href=\"http:\/\/msdn.com%22\"><span style=\"font-family: Consolas;font-size: small\">http:\/\/msdn.com&#8221;<\/span><\/a> <br \/><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br \/>&nbsp;&nbsp;&nbsp; End Function <br \/>&nbsp;&nbsp;&nbsp; Private Function GetTitle(input As String) As String <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Const startText = &#8220;&lt;title&gt;&#8221; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Const endText = &#8220;&lt;\/title&gt;&#8221;<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim startIndex = input.IndexOf(startText) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If startIndex = -1 Then <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Return &#8220;not found&#8221; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Else <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; startIndex += startText.Length <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim endIndex = input.IndexOf(endText, startIndex) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim result = input.Substring(startIndex, endIndex &#8211; startIndex) <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&amp;nbs\np;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Return result.Trim() <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; End If <br \/>&nbsp;&nbsp;&nbsp; End Function <br \/>End Module <br \/><\/span><\/p>\n<p>&nbsp;<\/p>\n<p>The above example displays all of the titles only when all of the tasks are complete. If you want to see the results before cancellation, you can replace GetWebPagesAsync with the following:<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">Private Async Function GetWebPagesAsync( _ <br \/>urls As List(Of String), <br \/>cts As CancellationTokenSource) As Task<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp; For Each webAddress In urls <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim client As New WebClient()<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dim theTask As Task(Of String) = <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; client.DownloadStringTaskAsync(New Uri(webAddress), cts.Token)<\/span><\/p>\n<p><span style=\"font-family: Consolas;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Await theTask <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine(GetTitle(theTask.Result)) <br \/>&nbsp;&nbsp;&nbsp; Next <br \/>End Function<\/span><\/p>\n<p>&nbsp;<\/p>\n<p>Thanks to Anthony Green, Alex Turner, Lucian Wischik, Mick Alberts, and Thomas Petchel for providing tech review.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>Resources<\/strong><\/p>\n<ul>\n<li><a href=\"http:\/\/msdn.microsoft.com\/en-US\/vstudio\/async\">Details and Download Page<\/a><\/li>\n<li><a href=\"http:\/\/social.msdn.microsoft.com\/Forums\/en-US\/async\/threads\">Forum for Feedback and Questions<\/a><\/li>\n<li><a href=\"http:\/\/connect.microsoft.com\/\">Microsoft Connect for Bugs and Suggestions<\/a><\/li>\n<li><a href=\"http:\/\/blogs.msdn.com\/b\/vbteam\/archive\/2011\/04\/13\/async-feature-control-flow.aspx\">Async Feature Control Flow blog<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>The new Async feature in the Visual Studio Async CTP (SP1 Refresh) provides an elegantly simple technique to make code asynchronous. Our writing team uses an internal app that would benefit from asynchronous calls.&nbsp; For each URL contained in the MSDN documentation that we publish, the app lists the title from the link, and the [&hellip;]<\/p>\n","protected":false},"author":260,"featured_media":8818,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[195],"tags":[25,36,37,39,120,149,162,171],"class_list":["post-543","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-visual-basic","tag-alan-berman","tag-async","tag-asynchronous-programming","tag-await","tag-parallel-programming","tag-task-cancellation","tag-vb","tag-visual-basic"],"acf":[],"blog_post_summary":"<p>The new Async feature in the Visual Studio Async CTP (SP1 Refresh) provides an elegantly simple technique to make code asynchronous. Our writing team uses an internal app that would benefit from asynchronous calls.&nbsp; For each URL contained in the MSDN documentation that we publish, the app lists the title from the link, and the [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts\/543","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/users\/260"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/comments?post=543"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts\/543\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/media\/8818"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/media?parent=543"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/categories?post=543"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/tags?post=543"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}