C# 5/Rx Extensions :Vers un développement asynchrone
Giovanni Clement WygwamEric Vernié Microsoft
Motivations
Point de rupture
La programmation parallèle est un art difficile
Example de code séquentiel (C#)
for (int z = 0; z < nbandesReel; z++) { int offSetpositiony = (tailleBandeY) * (z); a = xmin; b = (ymin) + (intigralY * tailleBandeY) * z; Bitmap bmp = DessinerBandeUnsafe(b,width ,tailleBandeY); Rectangle rect = new Rectangle(0, offSetpositiony, width, tailleBandeY); Gfx.DrawImage(bmp, rect);} //Code omis pour plus de clarté
Code parallèle manuelManualResetEvent mre = new ManualResetEvent(false); ReaderWriterLockSlim lck = new ReaderWriterLockSlim(); int z = 0; int restant = nbandesReel; for (z = 0; z < nbandesReel; z++) { param.b = (ymin) + (intigralY * tailleBandeY) * z; param.offSetpositiony = (tailleBandeY) * (z); ThreadPool.QueueUserWorkItem((object userState) => { Parametres p = (Parametres)userState; Bitmap bmp = this.DessinerBandeUnsafe(p.b, p.width, p.tailleBandeY); Rectangle rect = new Rectangle(0, p.offSetpositiony, p.width, p.tailleBandeY); try { lck.EnterWriteLock(); Gfx.DrawImage(bmp, rect); } finally { lck.ExitWriteLock(); } if (Interlocked.Decrement(ref restant) == 0) mre.Set(); }, param); } mre.WaitOne(); SynchroSurMandelbrotComplete(); }
Il est donc urgent de simplifier !!
Microsoft nous aide avec la Task Parallel Library
Task Parallel Library (.NET)
Parallel.For(0, nbandesReel, 1, (z) => { int offSetpositiony = (tailleBandeY) * (z); b = (ymin) + (intigralY * tailleBandeY) * z; Bitmap bmp = this.DessinerBandeUnsafe(b, width, tailleBandeY); Rectangle rect = new Rectangle(0, offSetpositiony, width, tailleBandeY); this.SyncroDessinerFractal(new DessinerEventArgs(bmp, rect, true));
});
Chaque itération est une tâche
Toutes les tâches doivent êtres finies avant de poursuivre
DEMOLes Tasks en action
Penser Parallèle
Décomposition du code
Modèle de Programmation Parallèle Visual Studio 2010
.NET C++
DonnéesTask Parallel Library
Parallel LINQParallel Patterns Library
Tâches Task Parallel Library Parallel Patterns Library
Flux Données Asynchronous Agents Library
L’offre parallèle de Visual Studio 2010
aujourd’hui
Nouvelle librairie TPL DataFlow
Pipeline
Coordination orientée événements
Modélisation orientée flux de données
Jeux de primitives (blocs) pour communiquer par message au sein d’un processus
1. Penser “Mise en tampon + traitement” 2. Construit au dessus des Tâches TPL, des collections concurrentes,
…3. Les “Blocs Dataflow” peuvent êtres mis en liaisons pour créer des
réseaux de communication
Repose sur des concepts / designs provenant1. Dix ans d’expérience en recherche en informatique2. Inspiré d’implémentations Microsoft
Asynchronous Agents library dans Visual C++ 2010 CCR de Microsoft Robotics
Introduction à TPL DataFlow
Nouvelle librairie orientée flux de données
Blocs de flux de données
Tampon Exécution
Jointure
Bloc pour stocker des donnéesBufferBlock<T>
private var _buffer = new BufferBlock<int>(); private static void Producer(){ while(true) { int item = Produce();
_buffer.Post(item); }}
private static async void ConsumerAsync(){ while(true) { int item = await _buffer.ReceiveAsync(); Process(item); }}
Bloc d’exécutionActionBlock<TInput>
var actionWrite = new ActionBlock<int>(item => Console.WriteLine(item));
var buffer = new BufferBlock<int>();
buffer.LinkTo(actionWrite);
for (int i = 0; i < 10; i++) buffer.Post(i);
Bloc d’ExécutionTransformBlock<TInput,TOutpu
t>
var transformPow = new TransformBlock<double, double>(d => Math.Pow(d, 2));var transformSqrt = new TransformBlock<double, double>(d => Math.Sqrt(d));var actionWrite = new ActionBlock<double>(d => Console.WriteLine(d));
transformPow.LinkTo(transformSqrt);transformSqrt.LinkTo(actionWrite);
for (double d = 0; d < 10; d++) transformPow.Post(d);
Bloc pour stocker des données BroadcastBlock<T>
var broadcast = new BroadcastBlock<int>(_ => _);
var buffer1 = new BufferBlock<int>();var buffer2 = new BufferBlock<int>();
broadcast.LinkTo(buffer2);broadcast.LinkTo(buffer1);
for (int i = 0; i < 10; i++){ broadcast.Post(i);}
for (int i = 0; i < 10; i++){ Console.WriteLine("Buffer1 " + buffer1.Receive()); Console.WriteLine("Buffer2 " + buffer2.Receive());}
DEMOTPL DataFlow en action
Penser parallèle et structurer l’application doit donc devenir une seconde
nature afin que l’application monte
efficacement en charge.
Mais développer parallèle, ce n’est pas que la
performance !!
C’est aussi l’asynchronisme
Asynchronous Programming Model (APM)1. IAsyncResult BeginRead(byte[] array,int offset,int
numBytes, AsyncCallback userCallback,object stateObject)
2. int EndRead(IAsyncResult asynResult)
Event-based Asynchronous Pattern (EAP)1. void SendAsync( IPAddress address, Object
userToken )2. pingSender.PingCompleted += new
PingCompletedEventHandler (PingCompletedCallback);
Modèles existant
Task-based Asynchronous Pattern (TAP)1.Task, et Task<Tresult>2.Nouvelles fonctionnalités du
langage C# 5 async et await
Reactive Extensions
Nouveaux Modèles
TAP : Comment ça marche ?
async Task<XElement> GetRssAsync(string url) { var client = new WebClient(); var task = client.DownloadStringTaskAsync(url); var text = await task; var xml = XElement.Parse(text); return xml;}
async Task<XElement> GetRssAsync(string url) { var client = new WebClient(); var task = client.DownloadStringTaskAsync(url); var text = await task; var xml = XElement.Parse(text); return xml;}
TAP Comment ça marche ?Task<XElement> GetRssAsync(string url) {
var $builder = AsyncMethodBuilder<XElement>.Create(); var $state = 0; TaskAwaiter<string> $a1; Action $resume = delegate { try { if ($state == 1) goto L1; var client = new WebClient(); var task = client.DownloadStringTaskAsync(url); $state = 1; $a1 = task.GetAwaiter(); if ($a1.BeginAwait($resume)) return; L1: var text = $a1.EndAwait(); var xml = XElement.Parse(text); $builder.SetResult(xml); } catch (Exception $ex) { $builder.SetException($ex); } }; $resume(); return $builder.Task;}
DEMOTAP en action
• Bonne intégration avec Tasks• await sur méthodes de TaskEx (Run ou AwaitAll)• Transformation Task<T> en T au retour de await
• Support de l’annulation• CancellationTokenSource
• Gestion des exceptions• try/catch autour de await• OperationCanceledException pour annulation
C# Async composition
Reactive Extension
« Asynchronisme »Une tache est asynchrone, SSI son éxécution ne bloque pas le contexte applicatif dans lequel elle se trouve.
Reactive Extension
• Besoin d’une interface graphique fluide
• Consommation de services
• Applications connectées …
Reactive Extension
Rx Extensions <-> Reactive Extensions
Ensemble d’outils (extensions) facilitant la gestions de taches / contextes asynchrones dans vos applications.
Reactive Extension
FluentMethode chaining :
Nouveau context Dernier contextFacilité de débugging
Ubiquitaire
Producer / Consumers d’events Finit les multitudes de callbacks a gérer
Reactive Extension
Comment c’est fait ?
1 DLL principale : System.Reactive
Reactive Extension
Comment c’est fait ?
Utilise : Threading de la BCL (TPL) Collections Threadsafe Runtime objects
Reactive Extension
2 Interfaces à retenir :1. IObserver<T>2. IObservable<T>
Reactive Extension
Observable/Observer <T>• Souscription
• Enumerator action : OnNext Delegate se déroulant à chaque item.
• OnError Delegate se déroulant lors d’une erreur
• OnCompletedDelegate se déroulant lorsque la tache est terminée
Reactive Extension
Observable/Observer <T>• Souscription
• La séquence est envoyée vers les souscriptions
• A chaque donnée de séquence, le delegate reçoit l’item typé => C’est à vous d’y placer une logique.
• A chaque mise à jour de la source de données, les nouvelles séquences sont également envoyées.
Reactive Extension
Observable/Observer <T>• Linq to Observable
DEMORx Extensions : Premier pas
Reactive Extension
« Based events programming »• Utilisation des events comme
séquence de données requêtables.
• Ainsi, par exemple un evènement MouseMove (Forms) se transforme en une séquence de données, requétables.
Reactive Extension
« Based events programming »• L’ObservableCollection contient une
séquence de données, « live », correspondant au retour de l’évènement lié.
• Il devient donc facile de consommer de façon asynchrone cette séquence de données, vers plusieurs « points » / souscriptions
DEMORx Extensions : Drag & drop
Reactive Extension
« Producers / Consumers »
• Une source IEnumerable<T> peut devenir Observable<T>.
• Une source Observable<T> peut être « publiée » vers des consommateurs.
• Une source Observable<T> peut être consommée de X manières, ou X est le nombre de souscriptions.
Reactive Extension
« Producers / Consumers »
• On peut imaginer un flux de données extrait d’un WebService :• 3 souscriptions dans l’application :
• L’onglet News de l’application• L’onglet Tag • L’onglet Catégories
• Chaque onglet est alors mis à jour en live.
Reactive Extension
Conclusion
• Reactive extensions = Reactive programming
• Facilite et simplifie l’écriture d’applications asynchrones ( WinForms, WPF, WPhone7…)
• Réponses à de nombreuses problématiques de développement asynchrones
Visual Studio Async CTP (SP1 Refresh)1. http://www.microsoft.com/downloads/en/details.aspx?Fam
ilyID=4738205d-5682-47bf-b62e-641f6441735b&displaylang=en
TPL Dataflow1. http://msdn.microsoft.com/en-us/devlabs/gg585582
Asynchronous Programing Model 1. http://msdn.microsoft.com/fr-fr/library/ms228969(v=VS.85
)
Event-Based Asynchronous Pattern 1. http://msdn.microsoft.com/fr-fr/library/hkasytyf(v=VS.85).
aspx
http://blogs.msdn.com/b/devpara/
Ressources
RxExtensionshttp://msdn.microsoft.com/en-us/data/gg577609
Introductionhttp://msdn.microsoft.com/en-us/data/gg577611
Sampleshttp://rxwiki.wikidot.com/101samples
Ressources