C# Workshop - Threading

Preview:

DESCRIPTION

That's the slides for our C# Workshop at Darmstadt University of Technology, powered by Microsoft Student Partners.

Citation preview

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?

Recommended