Parallele Softwareentwicklung mit .NET 4.0
Vorteile & Neuerungen und Einsatzmöglichkeiten der Parallel Extensions
Was ist parallele Programmierung?
Aufteilung einer Aufgabe in Teilaufgaben die parallel bearbeitet werden
Macht sich die Rechenkraft mehrerer Prozessoren / Cores zu nutzen
Wozu parallele Programmierung?
Single Core Systeme User Interface nicht blockieren Asynchrone Operationen Synchronisation
Wozu parallele Programmierung?
Server skalieren schon mit Multi-CPU
User Systeme besitzen Multi-Core
Ressourcen müssen genutzt werden
Statistik
15%
58%1%
25%
0% 0%
1 CPU 2 CPUs 3 CPUs 4 CPUs6 CPUs 7 CPUs 8 CPUs
Parallele Programmierung – vor .NET 4.0
Synchrones & Asynchrones Threading Nachteil Thread Overhead
ThreadPool Geeignet für Fire and Forget▪ Einmal gestartete Threads können nicht kontrolliert
werden Nachteile▪ Warten▪ Abbruch▪ Weitermachen▪ Debugging▪ Etc.
Von Threads zu Tasks
Threads und Tasks sind sehr ähnlich Threads
erlauben parallele Ausführung innerhalb eines Prozesses
Symbolisch die Arbeiter des Prozesses Tasks
basieren auf Threads Symbolisch die Arbeit die den Arbeitern
aufgetragen wird Mit der Task Parallel Library in .NET 4.0
eingeführt
ThreadPool
Global Queue
Program Thread
Worker Thread 1
Worker Thread 1
…
Item 1Item 2Item 3
Item 4
Item 5
Item 6
Thread Management: Starvation Detection Idle Thread Retirement
Task Management
Program Thread
Lock-Free
Global Queue
LocalWork-
Stealing Queue
Local Work-
Stealing Queue
Worker Thread 1
Worker Thread p
…
…
Task 1Task 2
Task 3Task 5
Task 4
Task 6
Thread Management: Starvation Detection Idle Thread Retirement Hill-climbing
Die Task Klasse
Integriert als Subset von System.Threading
Erstellbar durch Erstellung eines Task Objektes StartNew() Erstellt und startet einen Task
in einem Schritt Alternativ Parallel.Invoke() Interessant
Task.ContinueWith()
Der Action/Func Delegate
Beinhaltet eine Methode die keinen Rückgabewert besitzt
Kann mehrere Parameter besitzen
Task ohne Parameter und Rückgabewert
static void Main(string[] args){ Action Action1 = new Action(Compute); Task Task1 = new Task(Action1); Task1.Start();}static void Compute(){ Console.WriteLine("Computing..."); System.Threading.Thread.Sleep(1000); Console.WriteLine("Computing finished...");}
Action Delegat
Action dem Task Objekt zuweisen
Task Starten
Task mit Parameter und ohne Rückgabewert
static void Main(string[] args){ Action<object> Action1 = new Action<object>(Compute); Task Task1 = new Task(Action1, "Parameter 1"); Task1.Start();}static void Compute(object Parameter){ Console.WriteLine("Computing with Parameter " + Parameter + "..."); System.Threading.Thread.Sleep(1000); Console.WriteLine("Computing finished...");}
Action Delegat mit Parameter vom Typ object
Action dem Task Objekt zuweisen &
Parameter übrgeben
Parameter
verarbeiten
Task ohne Parameter mit Rückgabewert
static void Main(string[] args){ Func<string> Func1 = new Func<string>(Compute); Task<string> Task1 = new Task<string>(Func1); Task1.Start(); Console.WriteLine("Result was " + Task1.Result);}static string Compute(){ Console.WriteLine("Computing..."); System.Threading.Thread.Sleep(1000); Console.WriteLine("Computing finished..."); return "Result";}
Func Delegat mit
Rückgabetyp string
Func dem Task mit Rückgabetyp string
zuweisen
Rückgabewert abrufen
Wert zurückgeben
Passende Signatur
Task mit Parameter und Rückgabewert
static void Main(string[] args){ Func<object, string> Func1 = new Func<object, string>(Compute); Task<string> Task1 = new Task<string>(Func1, "Parameter1"); Task1.Start(); Console.WriteLine("Result was " + Task1.Result);}static string Compute(object Parameter){ Console.WriteLine("Computing with Parameter " + Parameter + "..."); System.Threading.Thread.Sleep(1000); Console.WriteLine("Computing finished..."); return "Result";}
Func Delegat mit Rückgabetyp
stringund Parameter
object
Func dem Task mit Rückgabetyp string
zuweisen & Parameter übergeben
Lambda Ausdrücke
Bilden anonyme Funktion die Ausdrücke und Anweisungen enthält
Syntax Wichtige Operatoren▪ => “wird zu”▪ () definiert Eingangsparameter▪ {} umschließt eine Reihe von Anweisungen
Werden oft zusammen mit LINQ verwendet Wichtig
Lambda Expressions können auf Variablen der einschließenden Methode / Typ zugreifen
Lambda Ausdrücke
delegate int del(int i);static void Main(string[] args){ del myDelegate = x => x * x; int j = myDelegate(5); //j = 25}
Delegat zum speichern der Anonymen Funktion
Lambda Ausdruckx wird zu x * x
Aufruf der Anonymen Funktion
mittels Delegat
TaskCreationOptions
Bietet Optionen für die Behandlung des Tasks durch den Taskmanager None PreferFairness LongRunning AttachedToParent
Task Abbruch
Ein heikles Thema mit Multithreading Ziele
Wahren von Teilergebnissen Vermeiden von Holzhammer wie Thread.Abort Zuverlässige und sichere Abbruchmethode
Tasks werden durch CancellationToken beendet Abbruch wird von CancellactionSource angefordert,
aber nicht unbedingt sofort ausgeführt Gewährleistet sicheres Abbrechen von Tasks Im wesentlichen wie Abortvariable
CancellationSource
Beinhaltet CancellationToken Threadsichere vom Framework zur
Verfügung gestellte Abortvariable Wird für Tasks verwendet, kann aber
auch für konventionelles Threading benutzt werden
Aufruf von CancellationSource.Cancel setzt CancellationToken.IsCancellationRequested auf true (irreversibel)
CancellationToken
Mehrere Möglichkeiten auf Cancel Request zu reagieren Polling▪ Regelmäßig IsCancellationRequested überprüfen
wenn Task als Ordnungsgemäß beendet markiert werden soll
▪ ThrowIfCancellationRequested aufrufen wenn Task als Cancelled markiert werden soll
Callback Methode▪ Wird ausgeführt wenn Token gecancelled wird
Wait Handle▪ Triggert wenn Token gecancelled wird
Exception Handling
AggregateException wird geworfen wenn eine oder mehrere Exceptions auftreten
Wird an den Thread geleitet der auf den Task joint bzw das Ergebnis abruft
Task ist Cancelled wenn OperationCancelledException geworfen wurde
Task ist Faulted wenn andere Exceptions geworfen wurden
Wichtig! Just My Code unter Debugoptionen deaktivieren
Die Parallel Klasse
Bietet Zugriff auf parallelisierte Schleifen und Regionen
Wichtige Methoden Parallel.Invoke() Parallel.For() Parallel.ForEach()
Parallel.Invoke
Leichtgewichtigste Tasking Art Wenig Code Wenig Kontrolle
Parallel.For
Ersetzt das for Schlüsselwort Gibt ParallelLoopResult zurück
Enthält Informationen über die letzte Iteration etc.
Einfache Parallel.For Schleife
Parallel.For(0, 100, (i) => { Console.WriteLine(i); DoStuff(); });
Zählerstartwert
Zählerendwert
Zählervariable
Schleifen abbrechen
ParallelLoopState liefert die Methoden Stop() ▪ Bricht die Iteration so schnell wie möglich ab
Break()▪ Wartet bis alle Iterationen in allen Threads
vor der aktuellen Iterationen abgeschlossen sind und bricht die Iteration dann ab,
Rückgabewert ParallelLoopResult liefert Informationen über das Resultat der Schleife
Erweiterte Parallel.For SchleifeParallelLoopResult ForResult = Parallel.For(0, 100, (i, LoopState) => { Console.WriteLine(i); DoStuff(); if (i > 20) { LoopState.Break(); } }); if (!ForResult.IsCompleted) { Console.WriteLine(ForResult.LowestBreakIteration); }
Rückgabewert
Rückgabewert
ParallelLoopState
Iteration Abbrechen
Ergebnis abfragen
Parallel.ForEach
Paralleles Äquivalent zur foreach Schleife
Verwendung wie Parallel.For Schleife
Einfache Parallel.ForEach Schleife
List<string> Books = new List<string>{"Die Kreutzersonate", "Anna Karenina", "Krieg und Frieden", "Der Tod des Ivan Illitsch", "Herr und Knecht"}; Parallel.ForEach<string>(Books, (Element) => { Console.WriteLine(Element); });
CollectionIterationselem
ent
Von LINQ zu PLINQ
LINQ – Language Integrated Query Codekomponente zur Abfrage von
Datenquellen PLINQ – Parallel Language Integrated
Query Erweiterung des LINQ Befehlssatzes um
parallele Elemente
ParallelEnumerable Klasse
AsParallel() Einstiegspunkt für PLINQ Legt fest dass der Rest des Querys
parallelisiert ausgeführt werden soll AsSequential()
Legt fest dass der Rest des Querys sequentiell ausgeführt werden soll
Einfacher PLINQ Query
var source = Enumerable.Range(1, 10000); var smallerfiftynumbers = from number in source.AsParallel() where number < 50 select number;
Zahlen von 1 bis 10000
Parallele Bearbeitung
Reihenfolge & PLINQ
Aufgrund von Parallelität ist die Reihenfolge nicht gewährleistet
Abhilfe schafft AsOrdered() Verarbeitet parallel Buffert und sortiert Einträge um
ursprüngliche Reihenfolge zu wahren
PLINQ Abbruch
AsParallel().WithCancellation(CancellationToken)
Bricht den Query ab wenn das Token ein Cancel Signal erhält
Datenstrukturen für parallele Programmierung
Thread-safe, scalable collections IProducerConsumerCollection<T
>▪ ConcurrentQueue<T>▪ ConcurrentStack<T>▪ ConcurrentBag<T>
ConcurrentDictionary<TKey,TValue>
Phases and work exchange Barrier BlockingCollection<T> CountdownEvent
Partitioning {Orderable}Partitioner<T>
▪ Partitioner.Create
Exception handling AggregateException
Initialization Lazy<T>
▪ LazyInitializer.EnsureInitialized<T>
ThreadLocal<T>
Locks ManualResetEventSlim SemaphoreSlim SpinLock SpinWait
Cancellation▪ CancellationToken{Source}
Visual Studio 2010 & Parallele Programmierung
Multithread Debugging früher lästig und umständlich
Mit Visual Studio 2010 gibt es Debug Fenster Parallele Aufgaben Parallele Stapel
Profiling von Multithread Anwendungen möglich Concurrency Visualizer
Parallele Aufgaben
Ähnlich wie Threads Fenster Listet Tasks statt Threads auf
Parallele Stacks
Zeigt Abhängigkeiten zwischen Threads
Thread oder Task Darstellung
Vielen Dank für die Aufmerksamkeit!
Qiong [email protected]