Sunday, November 14, 2010

Side-by-Side Asynchronous Programming Example in F#, C#, and VB

I was pleased to hear the announcement at PDC2010 by Anders Hejlsberg that C# and VB will likely be following the lead of F# by including asynchronous programming support. As Don Syme states in his post on this topic "the proposed design of C# and VB asynchronous programming is pleasant and simple, and heavily inspired by the corresponding feature of F#". In this post I will provide a simple example in F#, C#, and VB. The provided example is based on an example from a post by Don entitled "Async and Parallel Design Patterns in F#: Parallelizing CPU and I/O Computations". This example is also very similar to those provided in a series of posts on this topic by Tomas Petricek. I strongly recommend reading Don's post as well as Tomas's series.

F#:
open System
open System.IO
open System.Net

let getHtml url printStrategy =
    async { let request =  WebRequest.Create(Uri url)
            use! response = request.AsyncGetResponse()
            use stream = response.GetResponseStream()
            use reader = new StreamReader(stream)
            let contents = reader.ReadToEnd()
            do printStrategy url contents
            return contents }
 
let sites = ["http://www.bing.com";
             "http://www.google.com";
             "http://www.yahoo.com";
             "http://msdn.microsoft.com/en-us/fsharp/default.aspx"]

let printStrategy url (contents:string) =  
    printfn "%s - HTML Length %d" url contents.Length

let sitesHtml = Async.Parallel [for site in sites -> getHtml site printStrategy]
                |> Async.RunSynchronously

do printfn "\r\nProcess Complete\r\nPress any key to continue"

do Console.ReadLine() |> ignore

C#:
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using System.Linq;

namespace CSharpAsync
{
    class Program
    {
        static async Task<string> GetHtml(string url, 
            Action<string, string> printStrategy)
        {
            var request = WebRequest.Create(new Uri(url));
            using (var response = await request.GetResponseAsync())
            {
                using (var stream = response.GetResponseStream())
                {
                    using (var reader = new StreamReader(stream))
                    {
                        var contents = reader.ReadToEnd();
                        printStrategy.Invoke(url, contents);
                        return contents;
                    }
                }
            }        
        }

        static async Task ProcessSites(string[] sites, 
            Action<string, string> printStrategy)
        {
            var sitesHtml = 
                await TaskEx.WhenAll(
                    sites.Select(site => GetHtml(site, printStrategy)));
            Console.WriteLine("\r\nProcess Complete\r\nPress any key to continue");
        }

        static void Main(string[] args)
        {
            var sites = new[] { "http://www.bing.com",
                            "http://www.google.com",
                            "http://www.yahoo.com",
                            "http://msdn.microsoft.com/en-us/fsharp/default.aspx" };

            Action<string, string> printStrategy =
                (url, contents) =>
                    Console.WriteLine("{0} - HTML Length {1}", url, contents.Length);
            
            ProcessSites(sites, printStrategy).Wait();

            Console.ReadLine();
        }
    }
}

VB:
Imports System
Imports System.IO
Imports System.Net
Imports System.Threading.Tasks
Imports System.Linq

Module Module1

    Async Function GetHtml(ByVal url As String, ByVal printStrategy As Action(Of String, String)) As Task(Of String)
        Dim request = WebRequest.Create(New Uri(url))
        Using response = Await request.GetResponseAsync()
            Using stream = response.GetResponseStream()
                Using reader = New StreamReader(stream)
                    Dim contents = reader.ReadToEnd()
                    printStrategy.Invoke(url, contents)
                    Return contents
                End Using
            End Using
        End Using
    End Function

    Async Function ProcessSites(ByVal sites As String(), ByVal printStrategy As Action(Of String, String)) As Task
        Dim sitesHtml = _
            Await TaskEx.WhenAll(sites.Select(Function(site) GetHtml(site, printStrategy)))
        Console.WriteLine("{0}Process Complete{0}Press any key to continue", _
            Environment.NewLine)
    End Function

    Sub Main()
        Dim sites = New String() {"http://www.bing.com", _
                "http://www.google.com", _
                "http://www.yahoo.com", _
                "http://msdn.microsoft.com/en-us/fsharp/default.aspx"}

        Dim printStrategy As Action(Of String, String) = _
            Sub(url, contents) Console.WriteLine("{0} - HTML Length {1}", url, contents.Length)
        ProcessSites(sites, printStrategy).Wait()
        Console.ReadLine()
    End Sub

End Module