<?xml version="1.0" encoding="utf-8"?><rss version="2.0"><channel><title>c2i.fr - Thread</title><link>http://www.c2i.fr:80/Tags/Thread</link><description>c2i.fr - Thread</description><item><title>Etendre une API .NET existante pour supporter les opérations asynchrones</title><link>http://www.c2i.fr:80/actualites/etendre-une-api-.net-existante-pour-supporter-les-operations-asynchrones</link><description>&lt;p&gt;&lt;img src="/Media/Default/BlogPost/actualites/techHeadBrother.png" alt="" width="393" height="75" /&gt;&lt;/p&gt;
&lt;p&gt;Laurent Kemp&amp;eacute; (apr&amp;egrave;s 2 ans d'absence tout comme moi ;-)) vient de publier un article sur comment adapter une op&amp;eacute;ration synchrone en une op&amp;eacute;ration asynchrone avec le framework 4.0.&lt;/p&gt;
&lt;p&gt;(Il savait certainement que j'&amp;eacute;tait en train de pr&amp;eacute;parer un article du genre et s'est donc pr&amp;eacute;cipit&amp;eacute; pour publier le sien ;-))&lt;/p&gt;
&lt;p&gt;Ceci dit il applique sa m&amp;eacute;thodologie &amp;agrave; l'excellente biblioth&amp;egrave;que Open Source RestSharp.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.techheadbrothers.com/Astuces.aspx/etendre-api-dotnet-existante-supporter-operations-asynchrones"&gt;http://www.techheadbrothers.com/Astuces.aspx/etendre-api-dotnet-existante-supporter-operations-asynchrones&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;NB ; j'aurais utilis&amp;eacute; plut&amp;ocirc;t un Task.Factory que l'utilisation directe du pool de thread.&lt;/p&gt;
&lt;p&gt;Ceci dit, dans le m&amp;ecirc;me temps, Stephen Toub de l'&amp;eacute;quipe de pfx, &lt;a href="http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx"&gt;publie un billet sur&lt;/a&gt; : Faut-il ou non exposer un wrapper asynchrone pour ses m&amp;eacute;thodes synchrones ? (billet faisant suite &amp;agrave; &lt;a href="http://blogs.msdn.com/b/pfxteam/archive/2012/03/24/10287244.aspx"&gt;un pr&amp;eacute;c&amp;eacute;dent billet&lt;/a&gt; sur le m&amp;ecirc;me sujet).&lt;/p&gt;
&lt;p&gt;Donc en gros il conclu : c'est sympa d'exposer ses m&amp;eacute;thodes en asynchrone, mais c'est mieux de laisser la d&amp;eacute;cision &amp;agrave; son consommateur. Je pense que sa principale crainte, c'est que la plupart des personnes impl&amp;eacute;mentant ces m&amp;eacute;thodes en asynchrones tombent dans des pi&amp;egrave;ges assez sournois qui g&amp;eacute;n&amp;egrave;rent des bogues inattendus (il a peur surtout des deadlocks).&lt;/p&gt;</description><pubDate>Sun, 15 Apr 2012 05:51:48 GMT</pubDate><guid isPermaLink="true">http://www.c2i.fr:80/actualites/etendre-une-api-.net-existante-pour-supporter-les-operations-asynchrones</guid></item><item><title>Async/Await FAQ</title><link>http://www.c2i.fr:80/actualites/async-await-faq</link><description>&lt;p&gt;&lt;img style="padding: 10px; float: right;" alt="" src="/Media/Default/BlogPost/actualites/.netTimeline.png" width="300" height="200" /&gt;&lt;a href="http://blogs.msdn.com/b/pfxteam/archive/2012/04/12/10293335.aspx"&gt;Voici un billet de l'&amp;eacute;quipe de TPL&lt;/a&gt; tr&amp;egrave;s int&amp;eacute;ressant regroupant l'ensemble des questions que l'on peut se poser quand on veut programmez de fa&amp;ccedil;on asynchrone (ici avec .NET 4.5 et le fameux couple async/await).&lt;/p&gt;
&lt;p&gt;Bien entendu, en face de ces questions, il pr&amp;eacute;sente des liens vers les diff&amp;eacute;rentes r&amp;eacute;ponses (sinon, ca n'aurait aucun int&amp;eacute;r&amp;ecirc;t !!!).&lt;/p&gt;
&lt;p&gt;Il d&amp;eacute;crit notamment les m&amp;eacute;canismes qui d&amp;eacute;coulent de la d&amp;eacute;claration d'une m&amp;eacute;thode async (ce que fait le compilateur), si l'on peut marquer n'impoprte quelle m&amp;eacute;thode comme async, est-ce int&amp;eacute;ressant de cr&amp;eacute;er syst&amp;eacute;matiquement une m&amp;eacute;thode async pour une m&amp;eacute;thode syncro existante, dois-je invoquer la m&amp;eacute;thode Dispose , await utilise t'il le SynchronizationContext courant, quels sont les compatibilit&amp;eacute;s avec les autres mod&amp;egrave;les asynchrones (APM, EAP), etc.&lt;/p&gt;
&lt;p&gt;Bref, un article "de base" pour aborder la programmation asynchrone avec .NET 4.5.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description><pubDate>Fri, 13 Apr 2012 06:23:59 GMT</pubDate><guid isPermaLink="true">http://www.c2i.fr:80/actualites/async-await-faq</guid></item><item><title>Enregistrer ses logs par batch en asynchrône par Jeffrey Richter</title><link>http://www.c2i.fr:80/actualites/enregistrer-ses-logs-par-batch-en-asynchrone-par-jeffrey-richter</link><description>&lt;p&gt;Jeffrey Ritcher publi peu mais ses publications sont toujours tr&amp;egrave;s int&amp;eacute;ressantes.&lt;/p&gt;
&lt;p&gt;Sp&amp;eacute;cialiste du multithreading depuis de nombreuses ann&amp;eacute;es, son article discute d'une impl&amp;eacute;mentation de l'enregistrement de logs par lots.&lt;/p&gt;
&lt;p&gt;Le sc&amp;eacute;nario est le suivant : vous avez une application, vous voulez loguer des informations, mais au lieu de le faire au fil de l'eau, vous voulez le faire par lot et de fa&amp;ccedil;on asynchr&amp;ocirc;ne.&lt;/p&gt;
&lt;p&gt;Il analyse donc les pi&amp;egrave;ges dans lesquels on risque de tomber et propose une solution &amp;agrave; base de Spinlock pour &amp;eacute;viter de bloquer les thread.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.wintellect.com/CS/blogs/jeffreyr/archive/2012/04/09/asynchronous-batch-logging-without-blocking-threads.aspx"&gt;http://www.wintellect.com/CS/blogs/jeffreyr/archive/2012/04/09/asynchronous-batch-logging-without-blocking-threads.aspx&lt;/a&gt;&lt;/p&gt;</description><pubDate>Tue, 10 Apr 2012 06:37:15 GMT</pubDate><guid isPermaLink="true">http://www.c2i.fr:80/actualites/enregistrer-ses-logs-par-batch-en-asynchrone-par-jeffrey-richter</guid></item><item><title>Les threads dans Windows 8 Metro</title><link>http://www.c2i.fr:80/actualites/les-threads-dans-windows-8-metro</link><description>&lt;p&gt;&lt;a href="http://blogs.codes-sources.com/jay/archive/2012/03/25/pas-de-threads-pour-vous-dans-les-applications-style-metro.aspx"&gt;Jerome Laban a publi&amp;eacute; un petit article tr&amp;egrave;s int&amp;eacute;ressant&lt;/a&gt; ce week-end sur les threads dans Windows 8 Metro.&lt;/p&gt;
&lt;p&gt;Il note la suppression de la classe Thread de WinRT (ou plus exactement, la d&amp;eacute;cision de ne pas mettre cette classe dans WinRT) et l'obligation (avantageuse) d'utiliser Task &amp;amp; co.&lt;/p&gt;
&lt;p&gt;Un petit billet &amp;agrave; lire...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description><pubDate>Mon, 26 Mar 2012 07:58:53 GMT</pubDate><guid isPermaLink="true">http://www.c2i.fr:80/actualites/les-threads-dans-windows-8-metro</guid></item><item><title>Encapsuler un appel de web service avec les Tasks</title><link>http://www.c2i.fr:80/articles/encapsuler-un-appel-de-web-service-avec-les-tasks</link><description>&lt;p&gt;&lt;a href="http://www.c2idotnet.com/articles/pattern-d-appel-asynchrone-dans-vos-services" target="_blank"&gt;Dans le précédent article&lt;/a&gt;, je vous montrais comment prototyper un service pour qu’il puisse être consommer de façon asynchrone de façon “moderne”.&lt;/p&gt; &lt;p&gt;Seulement il se peut que votre service encapsule l’appel à un service WCF ou à un service Web “classique”.&lt;/p&gt; &lt;p&gt;Prenons un exemple de WebService tout simple :&lt;/p&gt;&lt;pre class="brush: csharp;"&gt;[ServiceContract]
public interface IService1
{
    [OperationContract]
    string GetLastClientName(string countryName);
}

public class Service1 : IService1
{

    public string GetLastClientName(string countryName)
    {
        if (countryName == "FR")
            return "Richard Clark";
        return null;
    }
}
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Je sais, ce n’est pas le service du siècle.&lt;/p&gt;
&lt;p&gt;Maintenant, dans le code de l’application cliente, ajouter une référence de service en vérifiant bien que Visual Studio génère les méthodes asynchrones :&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.c2idotnet.com/Media/Default/Windows-Live-Writer/Encapsuler-un-appel-de-web-service-avec-_F98D/WSasync.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="WSasync" border="0" alt="WSasync" src="http://www.c2idotnet.com/Media/Default/Windows-Live-Writer/Encapsuler-un-appel-de-web-service-avec-_F98D/WSasync_thumb.jpg" width="653" height="607"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Visual Studio génère donc les méthodes BeginGetLastClientName et EndGetLastClientName (il génère également GetLastClientNameAsync, mais ici, l’approche est évènementielle).&lt;/p&gt;
&lt;p&gt;Personnellement, j’ai toujours eu du mal avec ce pattern, je ne l’ai jamais trouvé “friendly”.&lt;/p&gt;
&lt;p&gt;Heureusement, Task permet d’encapsuler tout cela très facilement.&lt;/p&gt;
&lt;p&gt;On va donc créer une classe service qui sera un wrapper du service web. Et l’on va lui ajouter une méthode GetLastClientNameAsync qui retournera une Task&amp;lt;string&amp;gt; :&lt;/p&gt;&lt;pre class="brush: csharp;"&gt;public class MyService
{
    private readonly Service1Client _client = new Service1Client();
    
    public Task&amp;lt;string&amp;gt; GetLastClientNameAsync(string country)
    {
        return Task&amp;lt;string&amp;gt;.Factory.FromAsync(
            _client.BeginGetLastClientName,
            _client.EndGetLastClientName, 
            country, 
            null);
    }
}
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Et voilà, c’est tout. On peut maintenant utiliser notre web service de façon très simple et de façon asynchrone :&lt;/p&gt;&lt;pre class="brush: csharp;"&gt;var service = new MyService();

service.GetLastClientNameAsync("FR")
    .ContinueWith(t =&amp;gt;
{
    if (t.IsFaulted)
    {
        Console.WriteLine(t.Exception.InnerException.Message);
        return;
    }
    Console.WriteLine(t.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Cool non ?&lt;/p&gt;</description><pubDate>Fri, 17 Feb 2012 16:57:40 GMT</pubDate><guid isPermaLink="true">http://www.c2i.fr:80/articles/encapsuler-un-appel-de-web-service-avec-les-tasks</guid></item><item><title>Pattern d'appel asynchrone dans vos services</title><link>http://www.c2i.fr:80/articles/pattern-d-appel-asynchrone-dans-vos-services</link><description>&lt;p&gt;Imaginons une op&amp;eacute;ration ex&amp;eacute;cut&amp;eacute;e par un service qui risque de durer un certain temps (comme dirait Fernand Raynaud ;-))&lt;/p&gt;
&lt;p&gt;Vous avez alors deux options pour l'exposition de votre m&amp;eacute;thode :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mode synchrone&lt;/li&gt;
&lt;li&gt;mode asynchrone&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;/div&gt;
&lt;p&gt;Si vous &amp;ecirc;tes d&amp;eacute;veloppeur chez Microsoft pour Windows 8, on vous dira que si votre m&amp;eacute;thode risque de durer plus de 50ms, alors vous devez imp&amp;eacute;rativement exposer votre m&amp;eacute;thode de fa&amp;ccedil;on asynchrone. Et une bonne fa&amp;ccedil;on de nommer votre m&amp;eacute;thode, est de la suffixer par Async.&lt;/p&gt;
&lt;p&gt;Qui dit asynchrone dit &amp;eacute;galement comment le client va pouvoir consommer le r&amp;eacute;sultat.&lt;/p&gt;
&lt;h1&gt;Approche &amp;eacute;v&amp;egrave;nementielle&lt;/h1&gt;
&lt;p&gt;Une solution envisag&amp;eacute;e par les premi&amp;egrave;res versions du Framework .NET &amp;eacute;tait d&amp;rsquo;&amp;ecirc;tre signifi&amp;eacute; par un &amp;eacute;v&amp;egrave;nement.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On cr&amp;eacute;ait une instance d&amp;rsquo;une classe,&lt;/li&gt;
&lt;li&gt;On s&amp;rsquo;abonnait &amp;agrave; un &amp;eacute;v&amp;egrave;nement de l&amp;rsquo;objet,&lt;/li&gt;
&lt;li&gt;On appelait une m&amp;eacute;thode de la classe qui d&amp;eacute;clenchait l&amp;rsquo;&amp;eacute;v&amp;egrave;nement quand elle avait finie.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Exemple : System.Net.WebClient avec DownloadFileCompleted par exemple.&lt;/p&gt;
&lt;h1&gt;Approche incompr&amp;eacute;hensible&lt;/h1&gt;
&lt;p&gt;La 2&amp;egrave;me approche consiste a utiliser le pattern recommand&amp;eacute; par Microsoft (d&amp;egrave;s le Framework 1.0) qui repose sur le delegate AsyncCallback et l&amp;rsquo;interface IAsyncResult. Personnellement, malgr&amp;eacute; des ann&amp;eacute;es d&amp;rsquo;utilisation du framework, j&amp;rsquo;ai vraiment du mal avec ce pattern! (j&amp;rsquo;ai &amp;agrave; chaque fois besoin d&amp;rsquo;une documentation pour me rappeler comment elle fonctionne).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vous lancez votre m&amp;eacute;thode &amp;ldquo;longue&amp;rdquo; via un Beginxxx qui doit attendre dans ses arguments un AsyncCallBack&lt;/li&gt;
&lt;li&gt;Dans le AsyncCallback, vous r&amp;eacute;cup&amp;eacute;rez le r&amp;eacute;sultat via un Endxxx sous forme de IAsyncResult qui poss&amp;egrave;de le vrai r&amp;eacute;sultat.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bon, je n&amp;rsquo;irais pas plus loin&amp;hellip;&lt;/p&gt;
&lt;h1&gt;Approche &amp;ldquo;moderne&amp;rdquo;&lt;/h1&gt;
&lt;p&gt;Cette fois, gr&amp;acirc;ce aux derni&amp;egrave;res technologies impl&amp;eacute;ment&amp;eacute;es dans le Framework, on va pouvoir simplifier grandement cela.&lt;/p&gt;
&lt;p&gt;Notez que j&amp;rsquo;ai utilis&amp;eacute; le mot &lt;strong&gt;Framework&lt;/strong&gt;. Cela signifie que c&amp;rsquo;est possible d&amp;egrave;s le framework 4.0 qui int&amp;egrave;gre les nouvelles classes Task &amp;amp; co. Avec le futur Framework 4.5, je pourrais utiliser le mot &lt;strong&gt;Compilateur&lt;/strong&gt; car en utilisant les termes &amp;ldquo;async&amp;rdquo; et &amp;ldquo;await&amp;rdquo;, toute la plomberie expos&amp;eacute;e ci-dessous sera impl&amp;eacute;ment&amp;eacute;e.&lt;/p&gt;
&lt;p&gt;Bref, ici, on est avec le Framework 4.0. Donc imaginons un service, une classe, avec une m&amp;eacute;thode qui prend un certain temps. La seule astuce consiste &amp;agrave; cr&amp;eacute;er une nouvelle m&amp;eacute;thode, attendant les m&amp;ecirc;me arguments mais qui, au lieu de retourner un TResult, retourne un Task&amp;lt;TResult&amp;gt;. Exemple :&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;public string GetFullName(string firstName, string lastName)
{
    return string.Format("{0} {1}", firstName, lastName);
}

public Task&amp;lt;string&amp;gt; GetFullNameAsync(string firstName, string lastName)
{
    // TODO
}
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Il me semble (A CONFIRMER) que Microsoft avec Windows 8 va encore plus loin puisque dans notre cas, la m&amp;eacute;thode GetFullName sera private donc non expos&amp;eacute;e pour d&amp;rsquo;&amp;eacute;ventuels clients, parce qu&amp;rsquo;elle d&amp;eacute;passe les fameux 50ms.&lt;/p&gt;
&lt;p&gt;Remarquez que j&amp;rsquo;utilise comme convention de suffixer ma m&amp;eacute;thode par Async (l&amp;rsquo;id&amp;eacute;e ne vient pas de moi, je ne fais que copier les directives de MS &lt;img style="border-style: none;" class="wlEmoticon wlEmoticon-winkingsmile" alt="Clignement d'&amp;oelig;il" src="http://www.c2idotnet.com/Media/Default/Windows-Live-Writer/Pattern-dappel-asynchrone-dans-vos-servi_AC2A/wlEmoticon-winkingsmile_2.png" /&gt;). L&amp;rsquo;avantage, c&amp;rsquo;est qu&amp;rsquo;au moins on sait &amp;agrave; quoi on a affaire.&lt;/p&gt;
&lt;p&gt;Donc il nous faut maintenant impl&amp;eacute;menter notre m&amp;eacute;thode GetFullNameAsync.&lt;/p&gt;
&lt;p&gt;Ici, mon code est simple, et m&amp;ecirc;me simplissime.&lt;/p&gt;
&lt;p&gt;Mais vous pouvez imaginer que votre m&amp;eacute;thode GetFullName appelle par exemple un autre service qui interroge une base de donn&amp;eacute;es SQL Azure distante, donc qui potentiellement, risque de prendre du temps (non pas que SQL Azure soit lent, loin de moi l&amp;rsquo;id&amp;eacute;e, mais votre connexion r&amp;eacute;seau elle, peut tr&amp;egrave;s certainement avoir des rat&amp;eacute;s). Donc on pourrait dans ce cas &amp;eacute;crire :&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;public Task&amp;lt;string&amp;gt; GetFullNameAsync(string firstName, string lastName)
{
    return Task&amp;lt;string&amp;gt;.Factory.StartNew(() =&amp;gt; GetFullName(firstName, lastName));
}
&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est &amp;eacute;galement ce que j&amp;rsquo;appellerai un cas simple car tout se fait dans le m&amp;ecirc;me thread.&lt;/p&gt;
&lt;p&gt;L&amp;agrave; ou cela se complique, c&amp;rsquo;est si votre m&amp;eacute;thode (GetFullName) appelle d&amp;rsquo;autres m&amp;eacute;thodes qui peuvent lancer d&amp;rsquo;autres threads. Votre autre service peut par exemple appeler en interne son repository en asynchrone &amp;eacute;galement. Donc on risque d&amp;rsquo;avoir des enchainements de Task.&lt;/p&gt;
&lt;p&gt;Heureusement dans le Framework, vous avez la classe qui g&amp;egrave;re cela pour vous, j&amp;rsquo;ai nomm&amp;eacute; : TaskCompletionSource.&lt;/p&gt;
&lt;p&gt;Concr&amp;egrave;tement, cela donne ceci :&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;public Task&amp;lt;string&amp;gt; GetFullNameAsync(string firstName, string lastName)
{
    var tcs = new TaskCompletionSource&amp;lt;string&amp;gt;();

    // lancement asynchrone
    Task&amp;lt;string&amp;gt;.Factory
        .StartNew(() =&amp;gt; GetFullName(firstName, lastName))
        .ContinueWith(
            task =&amp;gt;
            {
                // une erreur ?  
                if (task.IsFaulted)
                {
                    tcs.TrySetException(task.Exception.GetBaseException());
                }
                // annul&amp;eacute; ?
                else if (task.IsCanceled)
                {
                    tcs.TrySetCanceled();
                }
                // on retourne le r&amp;eacute;sultat 
                else
                {
                    tcs.TrySetResult(task.Result);
                }
            });

    return tcs.Task;
}
&lt;/pre&gt;
&lt;p&gt;Remarquez l&amp;rsquo;utilisation des Tryxxx car il se peut qu&amp;rsquo;il y ai un appel concurrentiel de l&amp;rsquo;affectation du r&amp;eacute;sultat, exception ,etc.&lt;/p&gt;
&lt;p&gt;Pour le client cela devient simple (et c&amp;rsquo;est compatible avec les futurs async/await), ici avec resynchronisation dans le thread de l&amp;rsquo;UI :&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;service.GetFullNameAsync("Richard", "Clark")
    .ContinueWith(
       t =&amp;gt; Console.WriteLine(t.Result),
       TaskScheduler.FromCurrentSynchronizationContext());
&lt;/pre&gt;</description><pubDate>Fri, 17 Feb 2012 13:48:27 GMT</pubDate><guid isPermaLink="true">http://www.c2i.fr:80/articles/pattern-d-appel-asynchrone-dans-vos-services</guid></item><item><title>Gestion d'exception avec les Tasks</title><link>http://www.c2i.fr:80/articles/gestion-d-rsquo-exception-avec-les-tasks</link><description>&lt;p&gt;Nous avons vu dans &lt;a href="http://www.c2idotnet.com/articles/d-eacute-marrons-avec-les-tasks" target="_blank"&gt;un pr&amp;eacute;c&amp;eacute;dent article&lt;/a&gt; comment utiliser Task pour effectuer des op&amp;eacute;rations dans un autre thread.&lt;/p&gt;
&lt;p&gt;Malheureusement, comme tout le monde n&amp;rsquo;est pas parfait, il se peut que cette ex&amp;eacute;cution se passe mal et qu&amp;rsquo;une exception se produise. Reprenons notre application WPF :&lt;/p&gt;
&lt;pre class="brush: xml;"&gt;&amp;lt;StackPanel&amp;gt;
    &amp;lt;Button Content="Clic" Click="Button_Click" /&amp;gt;
    &amp;lt;TextBlock x:Name="infos"/&amp;gt;
&amp;lt;/StackPanel&amp;gt;
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Avec un code qui g&amp;eacute;n&amp;egrave;re une exception :&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;private void Button_Click(object sender, RoutedEventArgs e)
{
    infos.Text = "Ex&amp;eacute;cution en cours....";

    Task&amp;lt;string&amp;gt;.Factory
        .StartNew(
            () =&amp;gt;
                {
                    Thread.Sleep(500);
                    throw new InvalidOperationException("Pas cool !!!");
                    return DateTime.Now.ToString();
                })
        .ContinueWith(
            task =&amp;gt;
                {
                    infos.Text = task.Result;
                },
            TaskScheduler.FromCurrentSynchronizationContext());
}
&lt;/pre&gt;
&lt;p&gt;Autant le dire tout de suite : ce code, c&amp;rsquo;est MAL &lt;img style="border-style: none;" class="wlEmoticon wlEmoticon-winkingsmile" alt="Clignement d'&amp;oelig;il" src="http://www.c2idotnet.com/Media/Default/Windows-Live-Writer/Gestion-dexception-avec-les-Tasks_A959/wlEmoticon-winkingsmile_2.png" /&gt;&lt;/p&gt;
&lt;p&gt;Si vous distribuez ce code en mode release, vos clients risquent de se retrouver face &amp;agrave; une jolie petite fen&amp;ecirc;tre :&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.c2idotnet.com/Media/Default/Windows-Live-Writer/Gestion-dexception-avec-les-Tasks_A959/TaskError.jpg"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="TaskError" border="0" alt="TaskError" src="http://www.c2idotnet.com/Media/Default/Windows-Live-Writer/Gestion-dexception-avec-les-Tasks_A959/TaskError_thumb.jpg" width="528" height="350" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pas cool. Et surtout, cette fen&amp;ecirc;tre n&amp;rsquo;apparaitra pas imm&amp;eacute;diatement au clic sur le bouton. Comme rien ne se passera imm&amp;eacute;diatement, vous pouvez avoir un utilisateur qui cliquera une dizaine de fois (ce que j&amp;rsquo;appelle CA, la &amp;ldquo;Cliquette Aig&amp;uuml;e&amp;rdquo;) avant de voir cette fen&amp;ecirc;tre de la mort qui tue. Normalement, dans les secondes qui suivent, il vous appelle au t&amp;eacute;l&amp;eacute;phone en vous incendiant de divers mots que l&amp;rsquo;&amp;eacute;thique m&amp;rsquo;interdit de reproduire ici (oui ils sont comme cela mes clients &lt;img style="border-style: none;" class="wlEmoticon wlEmoticon-smilewithtongueout" alt="Tire la langue" src="http://www.c2idotnet.com/Media/Default/Windows-Live-Writer/Gestion-dexception-avec-les-Tasks_A959/wlEmoticon-smilewithtongueout_2.png" /&gt;).&lt;/p&gt;
&lt;p&gt;Y&amp;rsquo;a t&amp;rsquo;il une erreur ?&lt;/p&gt;
&lt;p&gt;La Task de la m&amp;eacute;thode ContinueWith poss&amp;egrave;de heureusement une propri&amp;eacute;t&amp;eacute; IsFaulted vous indiquant si il y a eu une ou des exceptions de d&amp;eacute;clench&amp;eacute;es dans votre code. Donc on pourrait modifier notre pr&amp;eacute;c&amp;eacute;dent code ainsi :&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;private void Button_Click(object sender, RoutedEventArgs e)
{
    infos.Text = "Ex&amp;eacute;cution en cours....";

    Task&amp;lt;string&amp;gt;.Factory
        .StartNew(
            () =&amp;gt;
                {
                    Thread.Sleep(500);
                    throw new InvalidOperationException("Pas cool !!!");
                    return DateTime.Now.ToString();
                })
        .ContinueWith(
            task =&amp;gt;
                {
                    if (task.IsFaulted)
                    {
                        infos.Text = string.Format("Il y a eu des erreurs");
                    }
                    else
                    {
                        infos.Text = task.Result;
                    }
                },
            TaskScheduler.FromCurrentSynchronizationContext());
}
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Mais non, cela n&amp;rsquo;est toujours pas correct. Pourquoi ? Si vous cliquez intensivement sur le bouton, vous verrez qu&amp;rsquo;&amp;agrave; terme, votre application se plantera telle une vache tentant de faire du saut &amp;agrave; l&amp;rsquo;&amp;eacute;lastique.&lt;/p&gt;
&lt;p&gt;&lt;span style="text-decoration: line-through;"&gt;Pourquoi la vache se plantera ? Parce qu&amp;rsquo;elle ne sait pas voler !&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Pourquoi votre code plantera ? Parce que le thread ne sera pas lib&amp;eacute;r&amp;eacute;. Il existera toujours une r&amp;eacute;f&amp;eacute;rence vers un TaskExceptionHolder qui emp&amp;ecirc;chera le garbage collector de lib&amp;eacute;rer votre Task, donc de supprimer le thread. Le seul moyen de le lib&amp;eacute;rer correctement, est simplement d&amp;rsquo;acc&amp;eacute;der &amp;agrave; la propri&amp;eacute;t&amp;eacute; Exception de votre Task :&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;private void Button_Click(object sender, RoutedEventArgs e)
{
    infos.Text = "Ex&amp;eacute;cution en cours....";

    Task&amp;lt;string&amp;gt;.Factory
        .StartNew(
            () =&amp;gt;
                {
                    Thread.Sleep(500);
                    throw new InvalidOperationException("Pas cool !!!");
                    return DateTime.Now.ToString();
                })
        .ContinueWith(
            task =&amp;gt;
                {
                    if (task.IsFaulted)
                    {
                        var err = task.Exception;
                        infos.Text = string.Format("Il y a eu des erreurs : {0}", err.Message);
                    }
                    else
                    {
                        infos.Text = task.Result;
                    }
                },
            TaskScheduler.FromCurrentSynchronizationContext());
}
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Cette fois ci, on tient le bon bout. Sauf que le message qui sera affich&amp;eacute; ne sera pas le message de votre exception (&amp;ldquo;Pas cool !!!&amp;rdquo;) mais : Une ou plusieurs erreurs se sont produites.&lt;/p&gt;
&lt;h1&gt;AggregateException&lt;/h1&gt;
&lt;p&gt;En r&amp;eacute;alit&amp;eacute;, la propri&amp;eacute;t&amp;eacute; Exception de votre Task est toujours une AggregateException qui poss&amp;egrave;de une propri&amp;eacute;t&amp;eacute; de type collection contenant l&amp;rsquo;ensemble des exceptions qui ont pu se produire dans votre code (votre thread peut cr&amp;eacute;er d&amp;rsquo;autres threads avec des erreurs, etc.). Donc, dans notre cas ou il n&amp;rsquo;y a qu&amp;rsquo;une exception qui peut se produire, on peut acc&amp;eacute;der directement &amp;agrave; cette exception via la propri&amp;eacute;t&amp;eacute; InnerException de l&amp;rsquo;Exception :&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;if (task.IsFaulted)
{
    var err = task.Exception.InnerException;
    infos.Text = string.Format("Il y a eu une erreur : {0}", err);
}
else
{
    infos.Text = task.Result;
}
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;AggregateException typ&amp;eacute;e&lt;/h1&gt;
&lt;p&gt;Reste que si l&amp;rsquo;on &amp;eacute;crit du code proprement, il faut utiliser le typage des exceptions.&lt;/p&gt;
&lt;p&gt;Tout d&amp;eacute;pend comment vous avez l&amp;rsquo;habitude de g&amp;eacute;rer les exceptions, mais pour ma part, si c&amp;rsquo;est une exception &amp;agrave; laquelle je ne m&amp;rsquo;attends pas, je fais planter l&amp;rsquo;application (je vous assure que c&amp;rsquo;est le meilleur moyen d&amp;rsquo;avoir un code &amp;ldquo;solide&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;Donc il faut g&amp;eacute;rer les exceptions auxquelles on s&amp;rsquo;attend. Pour cela, AggregateException poss&amp;egrave;de une m&amp;eacute;thode Handle qui attend un pr&amp;eacute;dicat. Dans ce pr&amp;eacute;dicat, vous pouvez dire si vous g&amp;eacute;rez ou non l&amp;rsquo;exception d&amp;eacute;clench&amp;eacute;e. Dans notre exemple, cela donne ceci :&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;if (task.IsFaulted)
{
    task.Exception.Handle(ex =&amp;gt;
                   {
                       if (ex.GetType() == typeof (InvalidOperationException))
                       {
                           infos.Text = "On s'y attendait &amp;agrave; celle l&amp;agrave;";
                           return true;
                       }
                       infos.Text = "Argggg!!!";
                       return false;
                   });
}
else
{
    infos.Text = task.Result;
}
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Notre pr&amp;eacute;dicat retourne True pour les InvalidOperationException et False pour les autres (ca plante).&lt;/p&gt;
&lt;p&gt;NB : AggregateException poss&amp;egrave;de une m&amp;eacute;thode Flatten qui retourne une AggregateException avec un seul niveau de profondeur des exceptions.&lt;/p&gt;</description><pubDate>Tue, 14 Feb 2012 13:24:49 GMT</pubDate><guid isPermaLink="true">http://www.c2i.fr:80/articles/gestion-d-rsquo-exception-avec-les-tasks</guid></item><item><title>Démarrons avec les Tasks</title><link>http://www.c2i.fr:80/articles/d-eacute-marrons-avec-les-tasks</link><description>&lt;p&gt;Que vous le vouliez ou non, le d&amp;eacute;veloppement multi-t&amp;acirc;che est maintenant une obligation pour toute nouvelle application. Il est donc vital d&amp;rsquo;en comprendre les m&amp;eacute;canismes et de s&amp;rsquo;y mettre le plus t&amp;ocirc;t possible.&lt;/p&gt;
&lt;p&gt;En attendant le .NET Framework 4.5 avec les nouveaux mots cl&amp;eacute;s async et await (qui seront disponibles pour C# et VB .NET), le d&amp;eacute;veloppement d&amp;rsquo;applications multi-thread a d&amp;eacute;j&amp;agrave; &amp;eacute;t&amp;eacute; largement simplifi&amp;eacute; avec l&amp;rsquo;apparition de l&amp;rsquo;espace de noms System.Threading.Tasks.&lt;/p&gt;
&lt;p&gt;Issu de travaux de Microsoft Research, Parallel FX et PLINQ ont &amp;eacute;t&amp;eacute; impl&amp;eacute;ment&amp;eacute;s au coeur du framework puisque ces classes se retrouvent dans mscorlib (montrant ainsi l&amp;rsquo;importance du d&amp;eacute;veloppement multi-t&amp;acirc;che).&lt;/p&gt;
&lt;p&gt;Nous allons voir dans ce premier article un premier type d&amp;rsquo;utilisation des Tasks.&lt;/p&gt;
&lt;h1&gt;Cr&amp;eacute;ation d&amp;rsquo;une Task&lt;/h1&gt;
&lt;p&gt;Une Task est un objet qui va nous permettre d&amp;rsquo;ex&amp;eacute;cuter une m&amp;eacute;thode dans un nouveau thread.&lt;/p&gt;
&lt;p&gt;Si la m&amp;eacute;thode est une action (ie ne retourne rien), c&amp;rsquo;est la classe Task, si la m&amp;eacute;thode est une fonction (Func) et qu&amp;rsquo;elle retourne un objet de type TResult, c&amp;rsquo;est la classe g&amp;eacute;n&amp;eacute;rique Task&amp;lt;TResult&amp;gt; qui est utilis&amp;eacute;e.&lt;/p&gt;
&lt;p&gt;Il y a plusieurs fa&amp;ccedil;ons de cr&amp;eacute;er une Task. La premi&amp;egrave;re est de tout faire &amp;ldquo;&amp;agrave; la main&amp;rdquo;, c&amp;rsquo;est-&amp;agrave;-dire en cr&amp;eacute;ant nous m&amp;ecirc;me l&amp;rsquo;instance de la classe, en d&amp;eacute;finissant l&amp;rsquo;action ou la fonction associ&amp;eacute;e, &amp;agrave; lancer l&amp;rsquo;ex&amp;eacute;cution asynchrone et &amp;agrave; attendre le r&amp;eacute;sultat. Exemple :&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;Console.WriteLine("1/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
var task = new Task(() =&amp;gt;
{
    Thread.Sleep(500);
    Console.WriteLine("2/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
});

Console.WriteLine("3/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
task.Start();

Console.WriteLine("4/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
task.Wait();
Console.WriteLine("5/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Ce qui donnera &amp;agrave; la sortie :&lt;/p&gt;
&lt;p&gt;1/ ThreadId=9&lt;br /&gt;3/ ThreadId=9&lt;br /&gt;4/ ThreadId=9&lt;br /&gt;2/ ThreadId=10 // dans un autre Thread&lt;br /&gt;5/ ThreadId=9&lt;/p&gt;
&lt;p&gt;Je sais, ce n&amp;rsquo;est pas tr&amp;egrave;s lisible puisque le code 2/ qui est plac&amp;eacute; avant 3/ et 4/ est ex&amp;eacute;cut&amp;eacute; apr&amp;egrave;s (async/await simplifieront la lecture), mais on voit que le code de la Task est ex&amp;eacute;cut&amp;eacute;e d&amp;egrave;s qu&amp;rsquo;on ex&amp;eacute;cute la m&amp;eacute;thode Start et on attend son r&amp;eacute;sultat gr&amp;acirc;ce &amp;agrave; Wait. (vous pouvez d&amp;rsquo;ailleurs d&amp;eacute;finir le temps d&amp;rsquo;attente maximal avec Wait : Wait(TimeSpan duree)).&lt;/p&gt;
&lt;p&gt;Une autre fa&amp;ccedil;on de faire (que je pr&amp;eacute;f&amp;egrave;re mais cela n&amp;rsquo;engage que moi), est d&amp;rsquo;utiliser la classe TaskFactory (et son g&amp;eacute;n&amp;eacute;rique TaskFactory&amp;lt;TResult&amp;gt;). Cette classe poss&amp;egrave;de la m&amp;eacute;thode StartNew qui fait tout le travail pour vous de cr&amp;eacute;ation de la Task et vous retourne cette Task. Un singleton de cette classe est m&amp;ecirc;me accessible gr&amp;acirc;ce &amp;agrave; la propri&amp;eacute;t&amp;eacute; static de Task : Factory. On peut donc &amp;eacute;crire :&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;Console.WriteLine("1/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
var task = Task.Factory.StartNew(
    (() =&amp;gt;
         {
             Thread.Sleep(500);
             Console.WriteLine("2/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
         }));

Console.WriteLine("3/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);

task.Wait();
Console.WriteLine("4/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
&lt;/pre&gt;
&lt;p&gt;Vous allez me dire, cela fait exactement la m&amp;ecirc;me chose et ce n&amp;rsquo;est pas plus lisible. Mais comme StartNew retourne la Task cr&amp;eacute;&amp;eacute;e, on a acc&amp;egrave;s &amp;agrave; sa m&amp;eacute;thode ContinueWith qui est ex&amp;eacute;cut&amp;eacute;e &amp;agrave; la fin de l&amp;rsquo;action (ou de la fonction) de la Task. On peut tr&amp;egrave;s bien simplifier le code de la fa&amp;ccedil;on suivante :&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;Console.WriteLine("1/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
Task.Factory
    .StartNew(
        (() =&amp;gt;
             {
                 Thread.Sleep(500);
                 Console.WriteLine("2/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
             }))
    .ContinueWith(
        task =&amp;gt;
            {
                Console.WriteLine("4/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
            });

Console.WriteLine("3/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Ce qui donne :&lt;/p&gt;
&lt;p&gt;1/ ThreadId=10&lt;br /&gt;3/ ThreadId=10&lt;br /&gt;2/ ThreadId=11 // dans un autre thread&lt;br /&gt;4/ ThreadId=12 // encore un autre thread&lt;/p&gt;
&lt;p&gt;On a donc bien l&amp;rsquo;impression d&amp;rsquo;une continuit&amp;eacute; du code.&lt;/p&gt;
&lt;h1&gt;Interaction avec l&amp;rsquo;UI&lt;/h1&gt;
&lt;p&gt;Imaginons maintenant que l&amp;rsquo;on souhaite effectuer une t&amp;acirc;che dans un autre thread et afficher son r&amp;eacute;sultat dans un textblock de notre application WPF.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai donc une simple Windows WPF avec un bouton et un textblock :&lt;/p&gt;
&lt;pre class="brush: xml;"&gt;&amp;lt;StackPanel&amp;gt;
    &amp;lt;Button Content="Clic" Click="Button_Click" /&amp;gt;
    &amp;lt;TextBlock x:Name="infos"/&amp;gt;
&amp;lt;/StackPanel&amp;gt;
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Et au clic sur le bouton, je lance une t&amp;acirc;che qui me retourne l&amp;rsquo;heure :&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;private void Button_Click(object sender, RoutedEventArgs e)
{
    infos.Text = "Ex&amp;eacute;cution en cours&amp;hellip;";

    Console.WriteLine("1/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);


    Task&amp;lt;string&amp;gt;.Factory
        .StartNew(
            () =&amp;gt;
                {
                    Thread.Sleep(500);
                    Console.WriteLine("2/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
                    return DateTime.Now.ToString();
                })
        .ContinueWith(
            task =&amp;gt;
                {
                    Console.WriteLine("3/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
                    infos.Text = task.Result;
                });

    Console.WriteLine("4/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
}
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Et l&amp;agrave; : C&amp;rsquo;EST LE DRAME !&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai une exception, InvalidOperationException, m&amp;rsquo;indiquant que je n&amp;rsquo;ai pas le droit de modifier mon UI dans un autre thread que le thread principal, le thread de l&amp;rsquo;UI.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est tout simplement, comme nous l&amp;rsquo;avons vu plus haut, que le code ex&amp;eacute;cut&amp;eacute; dans ContinueWith n&amp;rsquo;est pas le code principal. Il faut se resynchroniser avec le thread principal.&lt;/p&gt;
&lt;p&gt;Heureusement, Task nous permet de le faire simplement, gr&amp;acirc;ce &amp;agrave; une surcharge de ContinueWith :&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;private void Button_Click(object sender, RoutedEventArgs e)
{
    infos.Text = "Ex&amp;eacute;cution en cours&amp;hellip;";

    Console.WriteLine("1/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);


    Task&amp;lt;string&amp;gt;.Factory
        .StartNew(
            () =&amp;gt;
                {
                    Thread.Sleep(500);
                    Console.WriteLine("2/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
                    return DateTime.Now.ToString();
                })
        .ContinueWith(
            task =&amp;gt;
                {
                    Console.WriteLine("3/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
                    infos.Text = task.Result;
                },
            TaskScheduler.FromCurrentSynchronizationContext());

    Console.WriteLine("4/ ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
}
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Le TaskScheduler permet cette re-synchronisation. Simple non ?&lt;/p&gt;
&lt;p&gt;NB : A la place de &amp;ldquo;Execution en cours&amp;hellip;&amp;rdquo; vous pouvez faire apparaitre une petite animation pour signifier que vous &amp;ecirc;tes en train de travailler. N&amp;rsquo;oubliez pas de la faire disparaitre par la suite.&lt;/p&gt;
&lt;p&gt;NB Bis : nous verrons dans un prochain article comment g&amp;eacute;rer les exceptions qui peuvent se produire dans notre code asynchrone.&lt;/p&gt;</description><pubDate>Tue, 14 Feb 2012 10:48:34 GMT</pubDate><guid isPermaLink="true">http://www.c2i.fr:80/articles/d-eacute-marrons-avec-les-tasks</guid></item></channel></rss>