Post on 10-May-2015
description
Threading mit C#Threading
C# Workshop, TU Darmstadt 2008, Präsentation von Qiong Wu (Microsoft Junior Student Partner)
Threading heute
Todo:
Prozesse / Threads? Arbeiten mit Threads Erstellen von Threads Threads beenden Datenzugriff mit Threads Deadlocks ThreadPools Asnychrone Programmierung Timer Windows Forms & Threading
Prozesse
Ablaufende Programme Heißen unter Windows auch Tasks Erlauben einem Prozessor mehrere
Aufgaben „gleichzeitig“ zu bearbeiten Interprozesskommunikation Besitzen
Virtuellen Adressspeicher Ausführbaren Code Mindestens einen Ausführungsthread
Prozesse managen: Der Task Manager
Threads
Teilbereiche eines Prozesses Erlauben einem Prozess mehrere
Aufgaben „gleichzeitig“ zu bearbeiten.
Teilen sich virtuellen Adressspeicher und die Systemressourcen des Prozesses zu dem sie gehören
C# Programme starten automatisch Hauptthread
Prozesse vs. Threads
Beides Konzepte zur parallelen Programmierung
Prozesse sind unabhängige Ausführungseinheiten
Threads als logische Arbeitsteile eines Prozesses
Parallelisierung sowohl mit mehreren Prozessen als auch mehreren Threads theoretisch möglich
Over-Multi Process Coordination
Over-Multithreading
Standardfall
Nutzen für Programmierer?
Parallelisieren von Programmaufgaben
Nutzen von Multi-Threaded Hardware (Multicore, Multiprozessor)
Trennung von Benutzeroberfläche und Berechnung
Verteilte Systeme
Wann keine Threads benutzen?
Threads können die Performance beeinträchtigen (Thread Overhead)
Threads sind häufige Fehlerquellen Viele Threads bedeuten auch viel
Ärger Ergo: Threading nur dann einsetzen
wenn es sinnvoll ist!
Threading in C#.net
Multithreading in C# relativ einfach Unterstützt asynchrones und
synchrones Threading Wichtigstes Werkzeug:
System.Threading Namespace
Arbeiten mit Threads
Manuelles Erstellen von Threads Beenden von Threads Datenzugriffe koordinieren Asynchrone Programmierung ThreadPool verwenden
Erstellen von Threads
1. Methode für Threadeinstiegspunkt erstellen
2. ParametrizedThreadStart / ThreadStart Delegaten mit Verweis auf die Einstiegsmethode erstellen
3. Thread Objekt mit Delegaten erstellen
4. Optional: Threadeigenschaften festlegen
5. Thread starten
ParametrizedThreadStart / ThreadStart Delegate
ThreadStart für parameterlose Threadmethoden
ParametrizedThreadStart für Threadmethoden mit Parameter Parameter muss vom Typ object sein
Auszuführende Methode
static void Main(string[] args) { ThreadStart TestThreadStart = new ThreadStart(ThreadMethod); Thread TestThread = new Thread(TestThreadStart);
TestThread.Name = "Test Thread"; TestThread.Priority = ThreadPriority.Normal; TestThread.IsBackground = false;
TestThread.Start();
Console.ReadLine(); } static void ThreadMethod() { Console.WriteLine("Thread Run"); }
Threadoptionen
Beispiel: Parameterloser Thread
Kein Rückgabewert
Auszuführende Methode
static void Main(string[] args) { ParameterizedThreadStart TestThreadStart = new ParameterizedThreadStart(ThreadMethod); Thread TestThread = new Thread(TestThreadStart); TestThread.Name = "Test Thread"; TestThread.Priority = ThreadPriority.Normal; TestThread.IsBackground = false; TestThread.Start("Test"); Console.ReadLine(); } static void ThreadMethod(object Parameter) { Console.WriteLine("Thread Run with Parameter: " + Parameter.ToString()); }
Threadoptionen
Beispiel: Parametrisierter Thread
Start mit Parameter
Kein Rückgabewert object Parameter
Warten auf Threads
Unterscheidung zwischen Foreground Threads Background Threads
Thread.Join()
Beispiel: Foreground & Background Threads
Thread ForegroundThread = new Thread(new ThreadStart(ThreadMethod)); ForegroundThread.Start();
Thread BackgroundThread = new Thread(new ThreadStart(ThreadMethod)); BackgroundThread.IsBackground = true; BackgroundThread.Start();
Foreground Thread Verhindert Beenden
der Applikation
Background Thread kann nach
Beendigung aller Foreground Threads terminiert werden
Beispiel: Thread.Join
ThreadStart operation = new ThreadStart(ThreadMethod); Thread[] theThreads = new Thread[5]; for (int x = 0; x <= 4; ++x) { theThreads[x] = new Thread(operation); theThreads[x].Start(); } foreach (Thread oneThread in theThreads) { Console.WriteLine("Waiting for Thread to terminate"); oneThread.Join(); Console.WriteLine("Thread terminated"); }
Auf Threadende warten
Thread starten
Threadpriorität
Bestimmt die Vorrangregelung bei der Ausführung eines Threads
Thread.Priority Eigenschaft ThreadPriority Enum
Lowest BelowNormal Normal AboveNormal Highest
Beispiel: Threadpriorität
Thread NormalPriorityThread = new Thread(new ThreadStart(ThreadMethod)); Thread HighPriorityThread = new Thread(new ThreadStart(ThreadMethod)); HighPriorityThread.Priority = ThreadPriority.Highest;
NormalPriorityThread.Start(); HighPriorityThread.Start();
Standardpriorität ist ThreadPriority.Normal
Setzen der Threadpriorität auf
ThreadPriority.Highest
Threads beenden
Thread.Abort() Löst ThreadAbortException aus Beendet Thread nach Auslösen der
Exception Sicheres Beenden von Threads
ThreadAbortException behandeln Alternative: Abortvariable
Beispiel: Thread.Abort()
static void Main(string[] args) { Thread newThread = new Thread(new ThreadStart(ThreadMethod)); newThread.Start(); Thread.Sleep(1000); newThread.Abort(); Console.ReadLine(); }
Thread beenden
Beispiel: ThreadAbortException
static void ThreadMethod() { try { for (int i = 0; i < 100; i++) { Console.WriteLine("Thread - working."); Thread.Sleep(100); } } catch (ThreadAbortException e) { Console.WriteLine("Thread - caught ThreadAbortException - resetting."); Console.WriteLine("Exception message: {0}", e.Message); } finally { Console.WriteLine("Just one more message before I drop"); }
}
ThreadAbortException behandeln
Alternative: Abortvariable
Einige Programmieren raten von Thread.Abort ab
Gründe Thread.Abort kann Thread an jeder Stelle
abbrechen Finally Statements können übergangen
werden Ressourcen können ungeschlossen
bleiben Ansatz: Abortvariable einführen und
im Thread pollen
Beispiel: Abortvariable
static void TestManualAbort() { Thread newThread = new Thread(new ThreadStart(ManualAbortThreadMethod)); newThread.Start(); Thread.Sleep(1000); Abort = true; } static volatile bool Abort = false; static void ManualAbortThreadMethod() { while (true) { if (Abort == false) { Console.WriteLine(System.DateTime.Now.ToString()); } else { break; } } }
Anmerkung
Probleme mit Thread.Abort() lassen sich auch ohne Abortvariable umgehen
Alternative Lösungen Code auf ThreadAbortException
anpassen Constrained Execution Regions einsetzen
(Fortgeschritten)
Fragen?Oder einfach Pause?
Datenzugriffe koordinieren
Vor Multithreading: Nur ein gleichzeitiger Zugriff auf Daten
Mit Multithreading: Möglichkeit des gleichzeitigen Zugriffs auf Daten von mehreren Threads
Was passiert wenn mehrere Threads gleichzeitig einen Datenbestand manipulieren?
Problem: Race Condition
Mehrere Threads können eine gemeinsame Variable manipulieren
Probleme beim Rückschreiben von Werten
Fehlerhafte Werte als Ergebnis
Beispiel: Race Condition
Thread 2X = X + 15
X = 0
Thread 1X = X + 30
Variable einlesen Variable einlesen
Beispiel: Race Condition
Thread 2X=0
X = 0
Thread 1X=0
Beispiel: Race Condition
Thread 2X=0
X = 0
Thread 1X=30
Beispiel: Race Condition
X = 0
Thread 1X=30
Variable schreiben
Thread 2X=15
Beispiel: Race Condition
X = 30
Thread 1Beenden…
Thread 2X=15
Variable schreiben
Beispiel: Race Condition
X = 15
Thread 2Beenden…
Beispiel: Race Condition
X = 15
Lösung des Problems
Interlocked Klasse Synchronisierungssperren
Interlocked Klasse
Bietet atomare Operationen für Variablen
Nur für triviale Operationen an einer Variable (Addieren, Dekrementieren, Vertauschen, Inkrementieren)
Effektiv, aber beschränktes Einsatzgebiet
Synchronisierungssperren
Monitor.Enter() + Monitor.Exit() Lock Schlüsselwort ReaderWriterLock Mutex / Semaphore / AutoResetEvent
/ ManualResetEvent
Monitor.Enter() / Monitor.Exit()
Setzt in Codeabschnitt eine Synchronisierungssperre
Synchronisieren auf ein Objekt Besitzer des Monitor des Objekts erhält
exklusiven Zugriff auf Codeabschnitt Andere blockieren bis Freigabe des
Monitors
Lock Schlüsselwort
Synchronisierungsobjekt als Parameter
Umschließt Codebereich implizit mit Monitor.Enter() und Monitor.Exit()
Beispiel: Monitor.Enter() / Monitor.Exit()
public static object Lockvar = new object(); public static void LockMethod(object Parameter) { Monitor.Enter(Lockvar); try { count++; Console.WriteLine("Thread " + count + " Writing"); finally { Monitor.Exit(Lockvar); } }
Sperrvariable
Anfang Synchronisierungssperr
e
Ende Synchronisierungssperr
e
Beispiel: Lock Schlüsselwort
public static object Lockvar = new object(); public static void LockMethod(object Parameter) { lock (Lockvar) { count++; Console.WriteLine("Thread " + count + " Writing"); } }
Sperrvariable
Sperrvariable
Deadlocks
Zwei Threads schließen sich gegenseitig durch Synchronisationssperren aus
Thread A wartet auf Thread B, Thread B wartet auf Thread A
Endloses Warten
Beispiel: Deadlocks
public static object Lockvar1 = new object(); public static object Lockvar2 = new object(); public static void DeadLockMethodOne() { lock (Lockvar1) { lock (Lockvar2) { Console.WriteLine("First"); } } } public static void DeadLockMethodTwo() { lock (Lockvar2) { lock (Lockvar1) { Console.WriteLine("Second"); } } }
Sperrvariable
Sperrt Lockvar1
Sperrt Lockvar2
Lösung?
Sorgfältig Programmieren! Monitor.TryEnter() mit Zeitlimit Kurze Codeabschnitte sperren
ReaderWriterLock
ReaderWriteLock Klasse ermöglicht Ausschließliche Lesesperre Schreibsperre Wechsel von Lese zu Schreibsperre und
vice versa
Fragen?Oder noch eine Pause?
ThreadPool
Erstellung eigener Threads oft unnötig
ThreadPool verwaltet Threads die auf Abruf bereit stehen, Standardanzahl 25
Overhead bei Threaderstellung und Threadvernichtung eliminiert
ThreadPool Threads sind Hintergrundthreads
ThreadPool Arbeitsaufgaben können nicht durch Fremdeinwirkung abgebrochen werden
Arbeiten mit ThreadPool
ThreadPool.QueueUserWorkItem Übergabe von Verweis auf Funktion Optional: Übergabe von Parameter
ThreadPool.GetMaxThreads Anzahl der maximal verfügbaren
Threads abrufen ThreadPool.SetMaxThreads
Anzahl der maximal verfügbaren Threads festlegen
Thread in ThreadPool erstellen
WaitCallback workItem = new WaitCallback(ThreadMethod);
if (!ThreadPool.QueueUserWorkItem(workItem, "ThreadPooled")) { Console.WriteLine("Element konnte nicht in Warteschlange gestellt werden"); }
WaitCallback DelegateThread mit Parameter
starten
Asynchrone Programmierung Idee: Asynchrones Starten & Beenden eines
Ablaufs Vorteile
Keine manuelle Verwaltung des Threads notwendig Automatische Threadskalierung Performancegewinn durch automatische Nutzung
von ThreadPool 3 Verwendungsmodelle
Warten-bis-fertig Modell Pollingmodell Rückrufmodell
Warten-bis-fertig Modell
Asynchrone Operation starten Andere Arbeiten erledigen Mit EndXXX auf Ende der Operation
warten
Beispiel: Warten bis fertig Modell
Console.WriteLine("Start Async Fetchin.."); GenericMethodDelegate<int> method = IntFetcher.Fetch; IAsyncResult ar = method.BeginInvoke(null, method);
Thread.Sleep(1000);
Console.WriteLine("Got " + method.EndInvoke(ar)); Console.WriteLine("End Async Fetchin..");
Methode asynchron ausführen
Andere Arbeiten ausführen
Auf Ende der Methode warten
Pollingmodell
Starten der asnychronen Operation Regelmäßig überprüfen ob die
Operation abgeschlossen ist Nach Abgeschlossener Operation
EndXXX aufrufen
Beispiel: Pollingmodell
Console.WriteLine("Start Async Fetchin.."); IAsyncResult ar2 = method.BeginInvoke(null, method);
while (!ar2.IsCompleted) { Thread.Sleep(1000); }
Console.WriteLine("Got " + method.EndInvoke(ar2)); Console.WriteLine("End Async Fetchin..");
Methode asynchron ausführen
Andere Arbeiten ausführen
Regelmäßig pollen
Ergebnis abrufen (keine Wartezeit)
Rückrufmodell
Starten der asynchronen Operation mit Übergabe einer Rückruffunktion
Beispiel: Rückrufmodell
Console.WriteLine("Start Async Fetchin.."); IAsyncResult ar3 = method.BeginInvoke(new AsyncCallback(CompleteFetch), method);
Thread.Sleep(1000);
static void CompleteFetch(IAsyncResult result) { GenericMethodDelegate<int> method = (GenericMethodDelegate<int>)result.AsyncState; int i = method.EndInvoke(result); Console.WriteLine("Got " + i); Console.WriteLine("End Async Fetchin..."); }
Ergebnis abrufen (keine Wartezeit)
Andere Arbeiten ausführen
Callback Funktion angeben
Fehlerbehandlung
Try Catch bei Aufruf der EndXXX Methode anwenden
try { Console.WriteLine("Got " + method.EndInvoke(ar2)); } catch { Console.WriteLine("Error occured"); } finally { Console.WriteLine("End Async Fetchin.."); }
Asynchrone Methoden implementieren
Delegaten erstellen BeginInvoke des Delegaten aufrufen EndInvoke des Delegaten aufrufen
Beispiel: Asynchrone Methoden implementieren
public delegate TOutput GenericMethodDelegate<TOutput>();
static void TestAsync() { Console.WriteLine("Start Async Fetchin.."); GenericMethodDelegate<int> method = IntFetcher.Fetch; IAsyncResult ar = method.BeginInvoke(null, method); Thread.Sleep(1000); Console.WriteLine("Got " + method.EndInvoke(ar)); Console.WriteLine("End Async Fetchin.."); }
Delegat auf auszuführende
Methode
Methode dem Delegat zuweisen
Methode asynchron starten
Blockieren bis asynchrone Methode
beendet
System.Threading.Timer
Ruft eine Methode asynchron nach Zeitspanne auf
Wiederholt Aufruf der Methode nach Zeitspanne
Timer.Change
Beispiel: Threading.Timer
static void TestTimer() { Timer tm = new Timer(new TimerCallback(ThreadMethod), "Timer", 0, 1000); Thread.Sleep(1000); tm.Change(0, 100); }
TimerCallbackDelegate
Parameter
Tick Intervall
Startverzögerung
Intervall / Verzögerung ändern
Windows Forms & Threading
Häufigster Anwendungsfall für Threading: Rechenlast aus GUIs auslagern
Dem Hauptthread möglichst wenig Rechenlast zuordnen
Rechenlast in Hauptthread verursacht hängende GUIs
Problematik: Zugriff auf Steuerelemente von Threads aus
Threaded vs Unthreaded
private void cmdThreaded_Click(object sender, EventArgs e) { Thread FormsThread = new Thread(new ThreadStart(DoWork)); FormsThread.Start(); }
private void cmdUnthreaded_Click(object sender, EventArgs e) { DoWork(); }
Ausführung von komplexen Operationen im GUI Thread
HowNotTo: Direktes Manipulieren
private void button1_Click(object sender, EventArgs e) { Thread FormsThread = new Thread(new ThreadStart(ManipulateList)); FormsThread.Start(); }
private void ManipulateList() { listBox1.Items.Add("test"); }
Ungültiger Vorgang!
Lösung: Invoke Method
Direktes Manipulieren von Windows Forms Steuerelementen löst Exception aus
Mit Invoke den UI Thread auffordern Manipulationen vorzunehmen
Delegaten übergeben Asynchroner Aufruf ebenfalls möglich Optional: Eine Methode für Invoke
und Direkt mit InvokeRequired Eigenschaft
Beispiel: Invoke
private delegate void ManipulateListDelegate();
private void ManipulateList() { if (listBox1.InvokeRequired == true) { listBox1.Invoke(new ManipulateListDelegate(ManipulateList)); } else { listBox1.Items.Add("test"); } }
Delegat nach Funktion erstellen
Invoke benötigt?
Invoke aufrufen mit Verweis auf Methode
Manipulationen vornehmen
Fragen?