Note

Access to this page requires authorization. You can try signing in or .

Access to this page requires authorization. You can try .

How to: Wrap EAP Patterns in a Task

The following example shows how to expose an arbitrary sequence of Event-Based Asynchronous Pattern (EAP) operations as one task by using a TaskCompletionSource<TResult>. The example also shows how to use a CancellationToken to invoke the built-in cancellation methods on the WebClient objects.

Example

class WebDataDownloader
{

 static void Main()
 {
 WebDataDownloader downloader = new WebDataDownloader();
 string[] addresses = { "http://www.msnbc.com", "http://www.yahoo.com",
 "http://www.nytimes.com", "http://www.washingtonpost.com",
 "http://www.latimes.com", "http://www.newsday.com" };
 CancellationTokenSource cts = new CancellationTokenSource();

 // Create a UI thread from which to cancel the entire operation
 Task.Factory.StartNew(() =>
 {
 Console.WriteLine("Press c to cancel");
 if (Console.ReadKey().KeyChar == 'c')
 cts.Cancel();
 });

 // Using a neutral search term that is sure to get some hits.
 Task<string[]> webTask = downloader.GetWordCounts(addresses, "the", cts.Token);

 // Do some other work here unless the method has already completed.
 if (!webTask.IsCompleted)
 {
 // Simulate some work.
 Thread.SpinWait(5000000);
 }

 string[] results = null;
 try
 {
 results = webTask.Result;
 }
 catch (AggregateException e)
 {
 foreach (var ex in e.InnerExceptions)
 {
 OperationCanceledException oce = ex as OperationCanceledException;
 if (oce != null)
 {
 if (oce.CancellationToken == cts.Token)
 {
 Console.WriteLine("Operation canceled by user.");
 }
 }
 else
 {
 Console.WriteLine(ex.Message);
 }
 }
 }
 finally
 {
 cts.Dispose();
 }
 if (results != null)
 {
 foreach (var item in results)
 Console.WriteLine(item);
 }
 Console.ReadKey();
 }

 Task<string[]> GetWordCounts(string[] urls, string name, CancellationToken token)
 {
 TaskCompletionSource<string[]> tcs = new TaskCompletionSource<string[]>();
 WebClient[] webClients = new WebClient[urls.Length];

 // If the user cancels the CancellationToken, then we can use the
 // WebClient's ability to cancel its own async operations.
 token.Register(() =>
 {
 foreach (var wc in webClients)
 {
 if (wc != null)
 wc.CancelAsync();
 }
 });

 object m_lock = new object();
 int count = 0;
 List<string> results = new List<string>();
 for (int i = 0; i < urls.Length; i++)
 {
 webClients[i] = new WebClient();

 #region callback
 // Specify the callback for the DownloadStringCompleted
 // event that will be raised by this WebClient instance.
 webClients[i].DownloadStringCompleted += (obj, args) =>
 {
 if (args.Cancelled == true)
 {
 tcs.TrySetCanceled();
 return;
 }
 else if (args.Error != null)
 {
 // Pass through to the underlying Task
 // any exceptions thrown by the WebClient
 // during the asynchronous operation.
 tcs.TrySetException(args.Error);
 return;
 }
 else
 {
 // Split the string into an array of words,
 // then count the number of elements that match
 // the search term.
 string[] words = null;
 words = args.Result.Split(' ');
 string NAME = name.ToUpper();
 int nameCount = (from word in words.AsParallel()
 where word.ToUpper().Contains(NAME)
 select word)
 .Count();

 // Associate the results with the url, and add new string to the array that
 // the underlying Task object will return in its Result property.
 results.Add(String.Format("{0} has {1} instances of {2}", args.UserState, nameCount, name));
 }

 // If this is the last async operation to complete,
 // then set the Result property on the underlying Task.
 lock (m_lock)
 {
 count++;
 if (count == urls.Length)
 {
 tcs.TrySetResult(results.ToArray());
 }
 }
 };
 #endregion

 // Call DownloadStringAsync for each URL.
 Uri address = null;
 try
 {
 address = new Uri(urls[i]);
 // Pass the address, and also use it for the userToken
 // to identify the page when the delegate is invoked.
 webClients[i].DownloadStringAsync(address, address);
 }

 catch (UriFormatException ex)
 {
 // Abandon the entire operation if one url is malformed.
 // Other actions are possible here.
 tcs.TrySetException(ex);
 return tcs.Task;
 }
 }

 // Return the underlying Task. The client code
 // waits on the Result property, and handles exceptions
 // in the try-catch block there.
 return tcs.Task;
 }
Class WebDataDownLoader

 Dim tcs As New TaskCompletionSource(Of String())
 Dim nameToSearch As String
 Dim token As CancellationToken
 Dim results As New List(Of String)
 Dim m_lock As Object
 Dim count As Integer
 Dim addresses() As String

 Shared Sub Main()

 Dim downloader As New WebDataDownLoader()
 downloader.addresses = {"http://www.msnbc.com", "http://www.yahoo.com", _
 "http://www.nytimes.com", "http://www.washingtonpost.com", _
 "http://www.latimes.com", "http://www.newsday.com"}
 Dim cts As New CancellationTokenSource()

 ' Create a UI thread from which to cancel the entire operation
 Task.Factory.StartNew(Sub()
 Console.WriteLine("Press c to cancel")
 If Console.ReadKey().KeyChar = "c"c Then
 cts.Cancel()
 End If
 End Sub)

 ' Using a neutral search term that is sure to get some hits on English web sites.
 ' Please substitute your favorite search term.
 downloader.nameToSearch = "the"
 Dim webTask As Task(Of String()) = downloader.GetWordCounts(downloader.addresses, downloader.nameToSearch, cts.Token)

 ' Do some other work here unless the method has already completed.
 If (webTask.IsCompleted = False) Then
 ' Simulate some work
 Thread.SpinWait(5000000)
 End If

 Dim results As String() = Nothing
 Try
 results = webTask.Result
 Catch ae As AggregateException
 For Each ex As Exception In ae.InnerExceptions
 If (TypeOf (ex) Is OperationCanceledException) Then
 Dim oce As OperationCanceledException = CType(ex, OperationCanceledException)
 If oce.CancellationToken = cts.Token Then
 Console.WriteLine("Operation canceled by user.")
 End If
 Else
 Console.WriteLine(ex.Message)
 End If

 Next
 Finally
 cts.Dispose()
 End Try

 If (Not results Is Nothing) Then
 For Each item As String In results
 Console.WriteLine(item)
 Next
 End If

 Console.WriteLine("Press any key to exit")
 Console.ReadKey()
 End Sub

 Public Function GetWordCounts(ByVal urls() As String, ByVal str As String, ByVal token As CancellationToken) As Task(Of String())

 Dim webClients() As WebClient
 ReDim webClients(urls.Length)
 m_lock = New Object()

 ' If the user cancels the CancellationToken, then we can use the
 ' WebClient's ability to cancel its own async operations.
 token.Register(Sub()
 For Each wc As WebClient In webClients
 If Not wc Is Nothing Then
 wc.CancelAsync()
 End If
 Next
 End Sub)


 For i As Integer = 0 To urls.Length - 1
 webClients(i) = New WebClient()

 ' Specify the callback for the DownloadStringCompleted
 ' event that will be raised by this WebClient instance.
 AddHandler webClients(i).DownloadStringCompleted, AddressOf WebEventHandler

 Dim address As Uri = Nothing
 Try
 address = New Uri(urls(i))
 ' Pass the address, and also use it for the userToken 
 ' to identify the page when the delegate is invoked.
 webClients(i).DownloadStringAsync(address, address)
 Catch ex As UriFormatException
 tcs.TrySetException(ex)
 Return tcs.Task
 End Try

 Next

 ' Return the underlying Task. The client code
 ' waits on the Result property, and handles exceptions
 ' in the try-catch block there.
 Return tcs.Task
 End Function



 Public Sub WebEventHandler(ByVal sender As Object, ByVal args As DownloadStringCompletedEventArgs)

 If args.Cancelled = True Then
 tcs.TrySetCanceled()
 Return
 ElseIf Not args.Error Is Nothing Then
 tcs.TrySetException(args.Error)
 Return
 Else
 ' Split the string into an array of words,
 ' then count the number of elements that match
 ' the search term.
 Dim words() As String = args.Result.Split(" "c)
 Dim NAME As String = nameToSearch.ToUpper()
 Dim nameCount = (From word In words.AsParallel()
 Where word.ToUpper().Contains(NAME)
 Select word).Count()

 ' Associate the results with the url, and add new string to the array that 
 ' the underlying Task object will return in its Result property.
 results.Add(String.Format("{0} has {1} instances of {2}", args.UserState, nameCount, nameToSearch))
 End If

 SyncLock (m_lock)
 count = count + 1
 If (count = addresses.Length) Then
 tcs.TrySetResult(results.ToArray())
 End If
 End SyncLock
 End Sub

See also


Feedback

Was this page helpful?

Additional resources