ParallelExtensionsExtras Tour – #16 – Async Tasks for WebClient, SmtpClient, and Ping

Stephen Toub - MSFT

(The full set of ParallelExtensionsExtras Tour posts is available here.)

The Task Parallel Library isn’t just about CPU-bound operations.  The Task class is a great representation for any asynchronous operation, even those implemented purely as asynchronous I/O.

Task’s ability to represent arbitrary asynchronous operations without tying up threads is rooted in the TaskCompletionSource<TResult> class (previously discussed here), and this becomes a building block for creating an unlimited number of higher-level asynchronous operations represented as a Task. ParallelExtensionsExtras includes a wealth of such implementations, and for the next several blog posts in this ParallelExtensionsExtras Tour series, we’ll examine several implementations as well as how they can be used in your own projects that require asynchrony.

The ParallelExtensionsExtrasExtensionsEAP folder contains three useful files: WebClientExtensions.cs, SmtpClientExtensions.cs, and PingExtensions.cs.  As you might guess, these files contain extension methods for the System.Net.WebClient, System.Net.Mail.SmtpClient, and System.Net.NetworkInformation.Ping classes.  These extension methods mirror the existing asynchronous methods on these types (which all implement the Event-Based Asynchronous Pattern), but rather than forcing you to first register for some events and then call a method to kick off the download, they simply return task-based representations.  For example, WebClientExtensions provides the following method, which will download the data at the specified address and return a Task<byte[]> to represent the result:

public static Task<byte[]> DownloadDataTask(

    this WebClient webClient, string address);

 

With a method like this, you can very easily download a single file, but you can now also take advantage of the power of the Task class to, for example, download multiple files and perform a follow-up operation asynchronously only when all of the asynchronous downloads have completed, in this case outputting some diagnostic information about each download’s success or failure, e.g.

 

Task.Factory.ContinueWhenAll(new[]

{

    new WebClient().DownloadDataTask(“http://www.microsoft.com”),

    new WebClient().DownloadDataTask(“https://blogs.msdn.com/pfxteam”),

    new WebClient().DownloadDataTask(“https://msdn.com/concurrency”)

},

tasks =>

{

    // All tasks completed; look at the result for each

    foreach (var task in tasks)

    {

        // Print out information about each download

        Console.WriteLine(“Address: “ + task.AsyncState);

        if (task.IsFaulted)

            Console.WriteLine(“tFailed: {0}”,

                task.Exception.InnerException.Message);

        else

            Console.WriteLine(“tDownloaded {0} bytes”,

                task.Result.Length);

    }

});

 

As another example, the PingExtensions class provides the extension method:

 

    public static Task<PingReply> SendTask(
        this Ping ping, string hostNameOrAddress);

 

This makes it easy to send multiple pings asynchronously and work with the resulting task objects, e.g.

 

var addresses = new[]

{

    “localhost”,

    “microsoft.com”,

    “msdn.com”

};

 

var pings = (from address in addresses

             select new Ping().SendTask(address)).ToArray();

foreach (var ping in pings)

{

    // Print out each ping as it succeeds or fails

    ping.ContinueWith(t =>

    {

        Console.WriteLine(“Ping for {0}: {1}”, t.AsyncState,

            t.IsFaulted ?

                t.Exception.InnerException.Message :

                t.Result.Status.ToString());

    });

}

 

Or by taking advantage of the ContinueWhenAny method, we can send out several pings and take action just when the first ping completes, e.g.

var pings = (from address in addresses

             select new Ping().SendTask(address)).ToArray();

Task.Factory.ContinueWhenAny(pings,

    ping => Console.WriteLine(“First ping completed: “ + ping.Result.Address));

 

In our next post, we’ll continue our exploration of using tasks for asynchrony.

0 comments

Discussion is closed.

Feedback usabilla icon