After reading http://blogs.msdn.com/wesdyer/archive/2008/01/11/the-marvels-of-monads.aspx
I created a program that enables creating multiple web requests asynchronously. Even the response streams are read using non-blocking IO!

var requests = new[]
{
    WebRequest.Create("
http://www.google.com/"),
    WebRequest.Create("
http://www.yahoo.com/"),
    WebRequest.Create("
http://channel9.msdn.com/")
};

var pages =
    from request in requests select
// essentially we are defining the computation here 
// it does NOT run here
        from response in request.GetResponseAsync()
        let stream = response.GetResponseStream()
        from html in stream.ReadToEndAsync()
        select new { html, response };

foreach (var page in pages)
{
    page(d => // do this when the page data is ready
    {
        Console.WriteLine(d.response.ResponseUri.ToString());
        Console.WriteLine(d.html.Substring(0, 40));
        Console.WriteLine();
    });
} // for loop kicks off the calls and does NOT block the main thread.

Console.ReadKey();

The code reads like it is synchronous, but it's really not! You get the benefit of easy to read and write code that is also uses non-blocking IO. So it's not using up threads waiting for each request to return.

Check out my blog post for the code that implements the Continuation Monad and provides the methods required to enable the LINQ syntax.
There is no explicit thread at all in my code. It uses the BeginXXX/EndXXX async methods.

I think it's amazing how LINQ can be leveraged to things other than querying data - All thanks to its monadic under-pinings.