Monday, March 26, 2012

C# async examples WhenAll, WhenAny, WhenEverythingWithinTimeSpan

Monday, March 26, 2012 Posted by Andre Broers , , , , , , , 23 comments
In this blog I will continue with the async example I wrote in a previous blog post. I showed the new async library of dot.net 4.5. Which will also work with Mono. All the samples I show in this blog post will also work on the latest Mono. These methods can be handy to download from multiple sources like twitter and facebook. Sometimes we want to get all the messages from all sources. Sometimes we only the messages from the fastest (the first returning) source and sometimes we want all the messages that returned within a certain timespan. This where the new Async library with the WhenAll, WhenAny methods come in very handy.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Net.Http;
using System.Json;
using System.Threading;

namespace MyAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Start Main");
            List<Task<List<MyObject>> listoftasks = new List<Task<List<MyObject>>();
            listoftasks.Add(GetGoogle());
            listoftasks.Add(GetTwitter());
            listoftasks.Add(GetSleep());
            List<MyObject>[] arrayofanswers = Task.WhenAll(listoftasks).Result;
            List<MyObject> answer = new List<MyObject>();
            foreach (List<MyObject> answers in arrayofanswers)
            {
                answer.AddRange(answers);
            }
            foreach (MyObject o in answer)
            {
                Console.WriteLine("{0} - {1}", o.name, o.origin);
            }
            Console.WriteLine("Press <Enter>");
            Console.ReadLine();
        } 

        static async Task<List<MyObject>> GetGoogle() 
        {
            Console.WriteLine("Start GetGoogle");
            List<MyObject> l = new List<MyObject>();
            var client = new HttpClient();
            Task awaitable = client.GetAsync("http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=broersa"); 
            HttpResponseMessage res = await awaitable;
            Console.WriteLine("After GetGoogle GetAsync");
            dynamic data = JsonValue.Parse(res.Content.ReadAsStringAsync().Result);
            Console.WriteLine("After GetGoogle ReadAsStringAsync");
            foreach (var r in data.responseData.results)
            {
                l.Add(new MyObject() { name = r.titleNoFormatting, origin = "google" });
            }
            return l; 
        }

        static async Task<List<MyObject>> GetTwitter()
        {
            Console.WriteLine("Start GetTwitter");
            List<MyObject> l = new List<MyObject>();
            var client = new HttpClient();
            Task awaitable = client.GetAsync("http://search.twitter.com/search.json?q=broersa&rpp=5&include_entities=true&result_type=mixed");
            HttpResponseMessage res = await awaitable;
            Console.WriteLine("After GetTwitter GetAsync");
            dynamic data = JsonValue.Parse(res.Content.ReadAsStringAsync().Result);
            Console.WriteLine("After GetTwitter ReadAsStringAsync");
            foreach (var r in data.results)
            {
                l.Add(new MyObject() { name = r.text, origin = "twitter" });
            }
            return l;
        }

        static async Task<List<MyObject>> GetSleep()
        {
            Console.WriteLine("Start GetSleep");
            List<MyObject> l = new List<MyObject>();
            await Task.Delay(5000);
            l.Add(new MyObject() { name = "Slept well", origin = "sleep" });
            return l;
        } 
    }
}

When we run this code it will result in the following:
C:\Users\Andre\Documents\Visual Studio 11\Projects\MyAsync\MyAsync\bin\Debug>MyAsync.exe
Start Main
Start GetGoogle
Start GetTwitter
Start GetSleep
After GetGoogle GetAsync
After GetGoogle ReadAsStringAsync
After GetTwitter GetAsync
After GetTwitter ReadAsStringAsync
Andre Broers' personal blog - google
BroersA Blog - google
Installing Windows 8 Consumer Preview in VirtualBox » BroersA Blog - google
MVC3 Webgrid Html helper » BroersA Blog - google
@KoenDeuze ik had al hetzelfde. Kan niet gezond zijn... - twitter
Mooi weer buiten, maar ik word helemaal $%$#@% van ORM tools... - twitter
D1 70 min by broersa at Garmin Connect - Details: http://t.co/Y4kTdl8b via @AddThis - twitter
FileNotFoundException: Could not load file or assembly 'Oracle.DataAccess' http://t.co/GQ27AEzQ - twitter
"An error occurred when verifying security for the message." error in WCF Service http://t.co/4rKKgPPP - twitter
Slept well - sleep
Press <enter>

As we can see it will get all the sources. This will take approximatly 5 seconds because of the sleep of 5 seconds in the GetSleep.
If we only want the first (and fastest) result whe can use the Task.WhenAny method:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Net.Http;
using System.Json;
using System.Threading;

namespace MyAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Start Main");
            List<Task<List<MyObject>>> listoftasks = new List<Task<List<MyObject>>>();
            listoftasks.Add(GetGoogle());
            listoftasks.Add(GetTwitter());
            listoftasks.Add(GetSleep());
            List<MyObject> answer = Task.WhenAny(listoftasks).Result.Result;
            foreach (MyObject o in answer)
            {
                Console.WriteLine("{0} - {1}", o.name, o.origin);
            }
            Console.WriteLine("Press <Enter>");
            Console.ReadLine();
        } 
     
        static async Task<List<MyObject>> GetGoogle() 
        {
            Console.WriteLine("Start GetGoogle");
            List<MyObject> l = new List<MyObject>();
            var client = new HttpClient();
            Task<HttpResponseMessage> awaitable = client.GetAsync("http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=broersa");
            HttpResponseMessage res = await awaitable;
            Console.WriteLine("After GetGoogle GetAsync");
            dynamic data = JsonValue.Parse(res.Content.ReadAsStringAsync().Result);
            Console.WriteLine("After GetGoogle ReadAsStringAsync");
            foreach (var r in data.responseData.results)
            {
                l.Add(new MyObject() { name = r.titleNoFormatting, origin = "google" });
            }
            return l;
        }

        static async Task<List<MyObject>> GetTwitter()
        {
            Console.WriteLine("Start GetTwitter");
            List<MyObject> l = new List<MyObject>();
            var client = new HttpClient();
            Task<HttpResponseMessage> awaitable = client.GetAsync("http://search.twitter.com/search.json?q=broersa&rpp=5&include_entities=true&result_type=mixed");
            HttpResponseMessage res = await awaitable;
            Console.WriteLine("After GetTwitter GetAsync");
            dynamic data = JsonValue.Parse(res.Content.ReadAsStringAsync().Result);
            Console.WriteLine("After GetTwitter ReadAsStringAsync");
            foreach (var r in data.results)
            {
                l.Add(new MyObject() { name = r.text, origin = "twitter" });
            }
            return l;
        }

        static async Task<List<MyObject>> GetSleep()
        {
            Console.WriteLine("Start GetSleep");
            List<MyObject> l = new List<MyObject>();
            await Task.Delay(5000);
            l.Add(new MyObject() { name = "Slept well", origin = "sleep" });
            return l;
        }
    }
}


Results in the following:
C:\Users\Andre\Documents\Visual Studio 11\Projects\MyAsync\MyAsync\bin\Debug>MyAsync.exe
Start Main
Start GetGoogle
Start GetTwitter
Start GetSleep
After GetGoogle GetAsync
After GetGoogle ReadAsStringAsync
Andre Broers&#39; personal blog - google
BroersA Blog - google
Installing Windows 8 Consumer Preview in VirtualBox » BroersA Blog - google
MVC3 Webgrid Html helper » BroersA Blog - google
Press <Enter>
After GetTwitter GetAsync
After GetTwitter ReadAsStringAsync

This works fine, but sometimes we want the most results within a certain timespan. Like in a realtime mashup situation. To accomplish this we will need a timeout. We will let this take 2 seconds and after this it has to return data and skip the ones that are not finished yet. For example we want to query several search engines and merge the results. I have some other sources here, because most of the sources need application keys to query, which is a bit out of scope for this blog post. For this sample we take the above code and change it so that it will timeout after 2 seconds. The result is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Net.Http;
using System.Json;
using System.Threading;

namespace MyAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            var cts = new CancellationTokenSource();
            Console.WriteLine("Start Main");
            List<Task<List<MyObject>> listoftasks = new List<Task<List<MyObject>>();
            listoftasks.Add(GetGoogle(cts));
            listoftasks.Add(GetTwitter(cts));
            listoftasks.Add(GetSleep(cts));
            listoftasks.Add(GetxSleep(cts));

            List<MyObject>[] arrayofanswers = Task.WhenAll(listoftasks).Result;
            List<MyObject> answer = new List<MyObject>();
            foreach (List answers in arrayofanswers)
            {
                answer.AddRange(answers);
            }
            foreach (MyObject o in answer)
            {
                Console.WriteLine("{0} - {1}", o.name, o.origin);
            }
            Console.WriteLine("Press ");
            Console.ReadLine();
        } 

        static async Task<List<MyObject>> GetGoogle(CancellationTokenSource cts) 
        {
            try
            {
                Console.WriteLine("Start GetGoogle");
                List<MyObject> l = new List<MyObject>();
                var client = new HttpClient();
                Task awaitable = client.GetAsync("http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=broersa", cts.Token);
                HttpResponseMessage res = await awaitable;
                Console.WriteLine("After GetGoogle GetAsync");
                dynamic data = JsonValue.Parse(res.Content.ReadAsStringAsync().Result);
                Console.WriteLine("After GetGoogle ReadAsStringAsync");
                foreach (var r in data.responseData.results)
                {
                    l.Add(new MyObject() { name = r.titleNoFormatting, origin = "google" });
                }
                return l;
            }
            catch (TaskCanceledException)
            {
                return new List<MyObject>();
            }
        }

        static async Task<List<MyObject>> GetTwitter(CancellationTokenSource cts)
        {
            try
            {
                Console.WriteLine("Start GetTwitter");
                List<MyObject> l = new List<MyObject>();
                var client = new HttpClient();
                Task awaitable = client.GetAsync("http://search.twitter.com/search.json?q=broersa&rpp=5&include_entities=true&result_type=mixed",cts.Token);
                HttpResponseMessage res = await awaitable;
                Console.WriteLine("After GetTwitter GetAsync");
                dynamic data = JsonValue.Parse(res.Content.ReadAsStringAsync().Result);
                Console.WriteLine("After GetTwitter ReadAsStringAsync");
                foreach (var r in data.results)
                {
                    l.Add(new MyObject() { name = r.text, origin = "twitter" });
                }
                return l;
            }
            catch (TaskCanceledException)
            {
                return new List<MyObject>();
            }
        }

        static async Task<List<MyObject>> GetSleep(CancellationTokenSource cts)
        {
            try
            {
                Console.WriteLine("Start GetSleep");
                List<MyObject> l = new List<MyObject>();
                await Task.Delay(5000,cts.Token);
                l.Add(new MyObject() { name = "Slept well", origin = "sleep" });
                return l;
            }
            catch (TaskCanceledException)
            {
                return new List();
            }

        } 

        static async Task<List<MyObject>> GetxSleep(CancellationTokenSource cts)
        {
            Console.WriteLine("Start GetxSleep");
            List<MyObject> l = new List<MyObject>();
            await Task.Delay(2000);
            cts.Cancel();
            l.Add(new MyObject() { name = "Slept short", origin = "xsleep" });
            return l;
        } 

    }
}

When we run this the result is the following:
C:\Users\Andre\Documents\Visual Studio 11\Projects\MyAsync\MyAsync\bin\Debug>MyAsync.exe
Start Main
Start GetGoogle
Start GetTwitter
Start GetSleep
Start GetxSleep
After GetGoogle GetAsync
After GetGoogle ReadAsStringAsync
After GetTwitter GetAsync
After GetTwitter ReadAsStringAsync
Andre Broers' personal blog - google
BroersA Blog - google
Installing Windows 8 Consumer Preview in VirtualBox » BroersA Blog - google
MVC3 Webgrid Html helper » BroersA Blog - google
@KoenDeuze ik had al hetzelfde. Kan niet gezond zijn... - twitter
Mooi weer buiten, maar ik word helemaal $%$#@% van ORM tools... - twitter
D1 70 min by broersa at Garmin Connect - Details: http://t.co/Y4kTdl8b via @AddThis - twitter
FileNotFoundException: Could not load file or assembly 'Oracle.DataAccess' http://t.co/GQ27AEzQ - twitter
"An error occurred when verifying security for the message." error in WCF Service http://t.co/4rKKgPPP - twitter
Slept short - xsleep
Press <Enter>

Now only the data is collected that has returned within the two seconds timer. Exactly what we want in web mashup situation.

23 comments:

  1. do we all the time need, delay in case of when all..???
    i have the same scenarios to process multiple tasks and once finished continue the while loop.

    ReplyDelete
  2. Excellent post!!! The future of .net application development is on positive note. It offers huge career prospects for talented professionals all over the world. Training on .net technology will ensure good salary package. DOT NET Training in Chennai | .NET Training in Chennai

    ReplyDelete
  3. Thanks for sharing this informative content that guided me to know the details about the training offered in different technology.
    Microstrategy training in chennai

    ReplyDelete
  4. • such a good website and given to more information thanks! and more visit
    qtp training in chennai

    ReplyDelete
  5. Wonderful blog.. Thanks for sharing informative blog.. its very useful to me..

    iOS Training in Chennai

    ReplyDelete
  6. The expansion of internet and intelligence in the business process lead the way to a huge volume of data. It is important to maintain and process these data to be efficient in data handling.
    Selenium Training in Chennai
    Selenium Training Institutes in Chennai

    ReplyDelete