Julien Dollon | await is not ALWAYS your friend

A goal without a plan is just a wish

My blog in english is here.

Je m’appelle Julien, j'ai 27 ans et je suis un ingénieur logiciel. Je concois les produits et services de demain. J'ai entre autre participé au developpement de Windows, XBOX, Office, Surface chez Microsoft (Redmond) et je suis maintenant chez Amazon AWS (Seattle).

Je travaille dur pour un jour avoir un grand impact sur ce monde.

Mais comme il n’y a pas de titans à combattre, que les monstres n’existent pas, je fais du dev sur des produits, qui je l’espère, vous apporteront de la joie.

Plus d'infos sur moi ici.

 

View Julien  Dollon's profile on LinkedIn

Tous les posts de ce blog ne reflètent que mon opinion et pas celui de mes employeurs et clients.

 

Service d'evaluation de competences et de creation de site webs sur cahors.

await is not ALWAYS your friend

Hi guys,

I write this article after I discovered that some people on the internet use await everywhere and finally complain about performances totally fucked up.

My goal was to make some tests on async. Ok Async is just fuckin’ awesome in kind of use, but is it really good for perf?

My test is simple: I create a Client class. A provider (a static method) gives me all clients with the famous yield return/IEnumerable.

Every client has to call the method Initialize before that you can use it. Normally I have to put it on the constructor, but for this demo you have to call it explicitely.

    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch myStopWatch = new Stopwatch();
            myStopWatch.Start();

            foreach (var item in Client.GetClients())
            {
                item.Initialize();
            }

            Console.WriteLine("Time ellapsed(ms): " + myStopWatch.ElapsedMilliseconds);
            myStopWatch.Stop();
            Console.ReadLine();
        }
    }


    class Client
    {
        public static IEnumerable<Client> GetClients()
        {
            for (int i = 0; i < 100; i++)
            {
                yield return new Client() { Id = Guid.NewGuid() };   
            }
        }

        public Guid Id { get; set; }

        //This method has to be called before you use a client
        //For the sample, I don't put it on the constructor
        public void Initialize()
        {
            Sleep(500);
        }
    }

This code is synchronous, but because I used yield return, the foreach doesn’t wait for other clients and initializes everything in the current thread (50 000ms).

But as you know, the user experience will suck because the current thread will be blocked during a little amount of time.

So what is the solution? In WinRT it’s not possible to use background worker, the best practice is to use Task and await.

If we change the code to use this cool feature of C#5, the code will be like that:

foreach (var item in Client.GetClients())
{
	await item.Initialize();
}

Now we are happy because our code is executed in an other thread, the UI thread is not blocked.

Ok… but ! The loop will everytime wait that the client is initialized to initialize the next one….

We passed from 50 000ms to 55 000ms….. close to the same thing!

It’s really important to understand when await is useful or not.

If you want to initialize all your clients AND continue your program synchronously after the initialization of the first one, it’s better to use something like that:

Stopwatch myStopWatch = new Stopwatch();
myStopWatch.Start();
            
await Task.Run(() => {
    Task lastTask = null;
    foreach (var item in Client.GetClients())
    {
        lastTask = item.Initialize();
    }

    //We wait that the last task is completed
    lastTask.Wait();
});


Console.WriteLine("Time ellapsed(ms): " + myStopWatch.ElapsedMilliseconds);
myStopWatch.Stop();
Finally your program will run “synchronously-like”, in an other thread, and fast (this solution take 550 ms).

Enjoy!

EDIT: I verified that the last Task is finished because I know that each task has the same amount of time. But you can improve the wait by using a stack of task and wait for all. In this case, it’s still 550ms.

Posté le: Sep 24 2012, 00:06 | Commentaires
Catégorie(s): .NET | Général

blog comments powered by Disqus
Anciens commentaires (archive):