A group blog from members of the VB team
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. 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. We use the app to verify that URL links are valid.
The following example is a very simplified version of the relevant code, which does synchronous reads of multiple web pages.
Imports System.Net Imports System.Threading.Tasks
Module Module1 Sub Main() Dim urls As List(Of String) = BuildURLs()
Dim startTime = Date.Now
GetWebPagesSynchronous(urls) 'GetWebPagesAsync(urls).Wait()
Dim seconds = (Date.Now - startTime).TotalSeconds
Console.WriteLine() Console.WriteLine("Ended in " & seconds & " seconds") Console.ReadKey() End Sub
Private Sub GetWebPagesSynchronous(urls As List(Of String)) Dim client As New WebClient
For Each url In urls Dim text = client.DownloadString(New Uri(url)) Console.WriteLine(GetTitle(text)) Next End Sub
Private Function GetTitle(input As String) As String Const startText = "<title>" Const endText = "</title>"
Dim startIndex = input.IndexOf(startText) If startIndex = -1 Then Return "not found" Else startIndex += startText.Length Dim endIndex = input.IndexOf(endText, startIndex) Dim result = input.Substring(startIndex, endIndex - startIndex) Return result.Trim() End If End Function
Private Function BuildURLs() As List(Of String) Return New List(Of String) From { "http://www.microsoft.com", "http://msdn.com" } End Function End Module
The following method transforms the above example to use the Async feature. Starting with the above example, change Main to call GetWebPagesAsync(urls).Wait() instead of GetWebPagesSynchronous(urls).
In your project, add a reference to AsyncCtpLibrary.dll, which is in My Documents\Microsoft Visual Studio Async CTP\Samples.
Private Async Function GetWebPagesAsync(urls As List(Of String)) As Task
Dim theTasks As New List(Of Task(Of String))
For Each url In urls Dim client As New WebClient()
Dim theTask As Task(Of String) = client.DownloadStringTaskAsync(New Uri(url))
theTasks.Add(theTask) Next
' Wait until the tasks are done.
' The Await statement causes execution to immediately return ' to the calling method, returning a new task. When all of the ' tasks complete, execution continues within this method.
' TaskEx.WhenAll would normally be Task.WhenAll. ' It's TaskEx.WhenAll in the CTP only. Await TaskEx.WhenAll(theTasks)
For Each theTask In theTasks Console.WriteLine(GetTitle(theTask.Result)) Next End Function
For each URL in the list, the DownloadStringTaskAsync method returns a Task object that is then stored in a generic List. 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 IEnumerable(T), including List(T).
The code spins up all of these tasks without worrying about running out of resources from a language perspective. I was inclined to write code to carefully throttle the calls to conserve resources, but was told that is unnecessary.
On computer running Windows 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. For 100 URLs, the average was 39.5 seconds for synchronous, and 28.5 seconds for Async. With 100 URLs on another day, the average was 39.9 seconds for synchronous, and 13.5 seconds for Async. The results are specific to the conditions, and your results will vary.
Note that the speedup in the Async example is almost entirely from the parallel processing, not the asynchronous processing. The advantages of asynchrony are that it does not tie up multiple threads, and that it does not tie up the user interface thread.
Cancellation
The following example adds cancellation. 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.
This code creates a CancellationTokenSource that contains a CancellationToken. The same cancellation token is passed to every call to DownloadStringTaskAsync. The CancellationTokenSource is also used to invoke cancellation.
Imports System.Net Imports System.Threading Imports System.Threading.Tasks
ProcessAsyncCancellable(urls)
Private Sub ProcessAsyncCancellable(urls As List(Of String)) Dim cts As New CancellationTokenSource()
cts.Token.Register(Sub() Console.WriteLine("cancelling"))
Dim theTask As Task = GetWebPagesAsync(urls, cts)
' To cancel midstream, set cancelIt to True. Dim cancelIt = False If cancelIt Then ' Set the milliseconds before cancelling. Thread.Sleep(2000) cts.Cancel() Else theTask.Wait() End If End Sub
Private Async Function GetWebPagesAsync( _ urls As List(Of String), cts As CancellationTokenSource) As Task
For Each webAddress In urls Dim client As New WebClient()
Dim theTask As Task(Of String) = client.DownloadStringTaskAsync(New Uri(webAddress), cts.Token)
Await TaskEx.WhenAll(theTasks)
Private Function BuildURLs() As List(Of String) Return New List(Of String) From { "http://www.microsoft.com", "http://msdn.com" } End Function Private Function GetTitle(input As String) As String Const startText = "<title>" Const endText = "</title>"
Dim startIndex = input.IndexOf(startText) If startIndex = -1 Then Return "not found" Else startIndex += startText.Length Dim endIndex = input.IndexOf(endText, startIndex) Dim result = input.Substring(startIndex, endIndex - startIndex) Return result.Trim() End If End Function End Module
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:
Await theTask Console.WriteLine(GetTitle(theTask.Result)) Next End Function
Thanks to Anthony Green, Alex Turner, Lucian Wischik, Mick Alberts, and Thomas Petchel for providing tech review.
Resources
verry nice i would like to see more of these handy examples ,, for a fact i have the idea that we as VB coders would love to see some more adavanced features of our language