Partie 6 – Création de solutions professionnelles © Dunod 2010 – Visual C#2010
Partie 6 – Création de solutions professionnelles © Dunod 2010 – Visual C#2010 Étape par étape – John Sharp Chapitre 28 Accès parallèle aux données Au terme de ce chapitre, vous saurez : ■ Utiliser PLINQ pour paralléliser des requêtes LINQ coûteuses en temps. ■ Utiliser les classes de collection concurrentes parallèles pour maintenir des collections de données thread-safe. ■ Utiliser les primitives de synchronisation parallèles pour coordonner l’accès aux données manipulées par des tâches concurrentes. Au chapitre 27, « Introduction à la Bibliothèque parallèle de tâches », vous avez vu comment exploiter les nouvelles fonctionnalités du .NET Framework pour effectuer des opérations en parallèle. Dans les chapitres précédents vous avez également vu comment on peut accéder à des données d’une manière déclarative en utilisant LINQ (Language Integrated Query). Une requête typique LINQ génère un ensemble de résultats énumérable, et vous pouvez parcourir en série cet ensemble pour récupérer les données. Si la source de données utilisée pour générer l’ensemble de résultats com- prend un grand nombre d’enregistrements, l’exécution d’une requête LINQ peut prendre un certain temps. De nombreux systèmes de gestion de bases de données confrontés au problème d’optimisation des requêtes ré- solvent ce problème en utilisant des algorithmes qui décomposent le pro- cessus d’identification des données d’une requête en une série de tâches, exécutent ces tâches en parallèle, puis combinent les résultats lorsque les tâches ont terminé de générer l’ensemble de résultats complet. Les con- cepteurs de la Bibliothèque parallèle de tâches (TPL) ont décidé de fournir LINQ avec une fonctionnalité semblable, et le résultat se nomme Parallel LINQ, ou PLINQ. Vous allez étudier PLINQ dans la première partie de ce chapitre. Cependant, PLINQ n’est pas toujours la technologie la plus appropriée pour une application. Si vous créez vos propres tâches manuellement, vous devez vous assurer que les threads concurrents qui exécutent les tâches coordonnent leurs activités correctement. La TPL fournit des méthodes qui Chapitre 28 – Accès parallèle aux données permettent d’attendre que les tâches soient terminées, et vous pouvez utili- ser ces méthodes pour coordonner les tâches à un niveau très rudimen- taire. Mais que va-t-il se passer si deux tâches tentent d’accéder aux mêmes données et de les modifier ? Si deux tâches s’exécutent en même temps, leurs opérations peuvent se chevaucher et corrompre les données. Cette situation peut conduire à des erreurs qui sont difficiles à corriger, principalement à cause de leur imprévisibilité. Depuis la version 1.0, le Mi- crosoft .NET Framework a fourni des primitives que vous pouvez utiliser pour verrouiller les données et coordonner les threads, mais pour les utiliser de manière efficace, vous devez avoir une bonne compréhension de la ma- nière dont les threads interagissent. La TPL inclut des variantes de ces primi- tives et des classes de collection spécialisées qui peuvent synchroniser l’accès aux données à travers les tâches. Ces classes masquent la plupart de la complexité responsable de la coordination de l’accès aux données. Vous allez voir comment utiliser certaines de ces nouvelles primitives de synchronisation ainsi que les classes de collection dans la seconde moitié de ce chapitre. Utilisation de PLINQ pour paralléliser l’accès déclaratif aux données Dans les chapitres précédents, vous avez vu combien LINQ est puissant pour récupérer des données à partir d’une structure de données énumé- rable. Dans le .NET Framework 4.0, LINQ a été étendu en utilisant la techno- logie de la TPL pour vous aider à améliorer les performances et à paralléli- ser certaines opérations de requête. Ces extensions se nomment PLINQ. PLINQ fonctionne en divisant un ensemble de données en partitions, puis utilise des tâches pour récupérer les données qui correspondent aux cri- tères spécifiés par la requête pour chaque partition en parallèle. Les résul- tats récupérés pour chaque partition sont combinés en un seul ensemble de résultats énumérable lorsque les tâches sont terminées. PLINQ est idéal pour les scénarios qui impliquent des ensembles de données avec un grand nombre d’éléments, ou si le critère de requête implique des opéra- tions coûteuses et complexes. Un des principaux objectifs de PLINQ est d’être aussi peu intrusif que pos- Partie 6 – Création de solutions professionnelles © Dunod 2010 – Visual C#2010 Étape par étape – John Sharp sible. Si vous avez beaucoup de requêtes LINQ existantes, vous n’avez pas besoin de modifier votre code pour leur permettre de s’exécuter avec la dernière génération du .NET Framework. Pour faire cela, le .NET Framework inclut la méthode d’extension AsParallel que vous pouvez utiliser avec un objet énumérable. La méthode AsParallel retourne un objet ParallelQuery qui agit d’une manière similaire à l’objet énumérable original excepté qu’il fournit des implémentations parallèles de nombreux opérateurs LINQ, tels que join et where. Ces nouvelles implémentations des opérateurs LINQ sont basées sur la TPL et utilisent des algorithmes variés pour exécuter des parties de vos requêtes LINQ en parallèle lorsque c’est possible. Comme toujours dans le monde du calcul parallèle, la méthode AsParallel n’est pas magique. Vous ne pouvez pas garantir que votre code sera plus rapide ; tout dépend de la nature de vos requêtes LINQ et si les tâches qu’elles effectuent se prêtent à la parallélisation. Pour comprendre com- ment PLINQ fonctionne et les situations dans lesquelles il est utile, il est plus simple d’étudier des exemples. Les exercices des sections suivantes mon- trent deux scénarios simples. Utilisation de PLINQ pour améliorer les performances lors du parcours d’une collection Le premier scénario est simple. Considérez une requête LINQ qui parcourt une collection et récupère des éléments de la collection en se basant sur un calcul qui sollicite beaucoup le processeur. Cette forme de requête peut bénéficier de l’exécution parallèle tant que les calculs sont indépen- dants. Les éléments de la collection peuvent être divisés en un certain nombre de partitions ; le nombre exact dépend de la charge actuelle de l’ordinateur et du nombre de CPU disponibles. Les éléments de chaque partition peuvent être traités par un thread séparé. Lorsque toutes les parti- tions ont été traitées, les résultats peuvent être fusionnés. Toute collection prenant en charge l’accès aux éléments via un index, comme un tableau ou une collection qui implémente l’interface IList<T>, peut être gérée de cette façon. Chapitre 28 – Accès parallèle aux données Note Si les calculs nécessitent l’accès à des données partagées, vous devez synchroniser les threads. Cela peut imposer une surcharge et peut annuler les avantages de la parallélisation de la requête. Parallélisation d’une requête LINQ sur une simple collection 1. En utilisant Microsoft Visual Studio 2010, ouvrez la solution PLINQ, situé dans le dossier \Visual C Sharp Etape par étape\Chapitre 28\PLINQ de votre dossier Documents. 2. Dans l’Explorateur de solutions, faites un double clic sur Program.cs pour afficher le fichier dans la fenêtre Code. Il s’agit d’une application console. Le squelette de l’application a dé- jà été créé pour vous. La classe Program contient deux méthodes appelées Test1 et Test2 qui illustrent deux scénarios courants. La mé- thode Main appelle chacune de ces méthodes test à tour de rôle. Les deux méthodes test ont la même structure générale ; elles créent une requête LINQ, l’exécutent, et affichent le temps d’exécution. Le code de chacune de ces méthodes est presque complètement sé- paré des instructions qui créent et exécutent les requêtes. Vous allez ajouter ces instructions au fur et à mesure que vous progresserez dans ces exercices. 3. Recherchez la méthode Test1. Cette méthode crée un grand tableau d’entiers et le remplit avec un ensemble de nombres aléatoires entre 0 et 200. Le générateur de nombres aléatoires comporte une valeur seed, de manière à ce que vous obteniez le même résultat à chaque fois que vous exécutez l’application. Vous allez ajouter une requête LINQ qui récupère tous les nombres de ce tableau qui possède une valeur supérieure à 100. 4. Après le premier commentaire A FINALISER de cette méthode, ajoutez la requête LINQ illustrée ici en gras : // A FINALISER : Crée une requête LINQ qui récupère tous les nombres supérieurs à 100 var superieur100 = from n in nombres where TestSiVrai(n > 100) select n; Le test n > 100 n’est pas assez riche en calculs pour montrer les béné- Partie 6 – Création de solutions professionnelles © Dunod 2010 – Visual C#2010 Étape par étape – John Sharp fices de la parallélisation de cette requête, si bien que le code ap- pelle une méthode nommée TestSiVrai, qui le ralentit en effectuant une opération SpinWait. La méthode SpinWait fait en sorte que le processeur exécute continuellement une boucle d’instructions spé- ciales sans opération pendant une courte période de temps, ce qui occupe le processeur tout en ne lui faisant effectuer aucun travail (on appelle cela le spinning). La méthode TestSiVrai ressemble à ceci : public static bool TestSiVrai(bool expr) { Thread.SpinWait(1000); return expr; } 5. Après le deuxième commentaire A FINALISER de la méthode Test1, ajoutez le code suivant en gras : // A FINALISER : Exécute la requête LINQ, et enregistre les résultats dans un objet List<int> List<int> nombresSuperieurs100 = new List<int>(superieur100); Rappelez-vous que les requêtes LINQ utilisent l’exécution uploads/Management/ chapitre28visual-c-2010-etape-par-etape-plinq.pdf
Documents similaires










-
30
-
0
-
0
Licence et utilisation
Gratuit pour un usage personnel Attribution requise- Détails
- Publié le Mai 15, 2022
- Catégorie Management
- Langue French
- Taille du fichier 0.4082MB