Der Mainthread ist ein Vordergrundthread, er hält den Prozess am "Leben".
Hintergrundthreads
halten den Process nicht am Leben. Alle Hintergrundthread werden beim beenden des Vordergrundthreads beendet.
Thread-Instanzen haben eine
IsBackground-Eigenschaft, um den Thread als Hintergrundthread zu
starten. ThreadPool.QueuedUserWorkItem Threads sind immer
Hintergrundthreads.
Threadpool
Mehrere Threads starten
Um Threads
innerhalb seiner Anwendung zu starten ist die Klasse ThreadPool und
vorallem die darin anthaltene statische Methode QueueUserWorkItem eine
mögliche Variante. Die mit der Methode erstellten Threads sind
Hintergrund-Threads. D.h. die Eigenschaft IsBackground der
Thread-Instanz ist auf true gesetzt. Zudem werden alle Backgroundthreads
beim Beenden des "auslösenden" Vordergrundsthread automatische
beendendet. Der hierbei auftretende ungültige Zustand ist eine einfache
Form einer Race Condition. Threadpool wird häufig für Threads verwendet
die in kleiner und gar keiner Kommunikation mit dem Vordergrundthread
stehen.
Hinweise
25
Worker Threads per default (CLR2.0), ab SP1 250
SetMaxThreads
Thread
werden nach der Abarbeitung recycelt
GetAvailable Threads
Threads verwalten
Klasse
System.Threading.Thread
Ein
Thread kann eine Methode über zwei verschiedene Delegaten ausführen
ThreadStart: ohne Parameter
ParameterizedThreadStart:
mit einen Object-Typ Parameter
Ein Thread wird Instanziert und
übergibt ein Delegaten an den Konstruktor
Über den Aufruf der
Methode Start() wird der Thread gestartet
Der neu gestartete
Thread beendet sich automatisch nach der erfolgreichen Beendigung der
Funktion oder nach Aufruf der Methode Abort()
Eine Instanz
eines Threads hat mehrere Methoden zum senden verschiedener Kommandos.
Hier nur zwei der wichtigsten
Join(), Join(int timeout) -
Blockt den aktuellen Thread bis der gestartete neue Thread zurück zum
Aufrufer kommt. Die Überladung gibt zusätzlichen einen Timeout in
Millisekunden an.
Abort() - Throw a ThreadAbortException -
bricht den Thread ab
Als statische Methoden existieren zwei
wichtige
Thread.Sleep(int timeout) - Thread macht eine Pause
in Millisekunden, hier ist Wechsel des Kontextes erlaubt
Thread.SpinWait(int
iteration) - Thread wartet eine Anzahl von Iterationen. Diese Methode
ist für die Thread-Synchronisierung mit bzw. Monitor.Enter() nützlich,
hier ist ein Kontextwechsel nicht erlaubt
Hinweise
Der gesamte ExecutionContext, d.h. Security, Localization,
Transaction - Informationen werden automatisch an den neuen Thread
übertragen
Thread Synchronizing
Grundsätzlich geht es
bei der Thread-Synchronisierung um den Zugriff auf Resourcen durch
mehrere Threads. Zur Synchronisierung von Thread gibt es mehrere
Möglichkeiten.
Monitor - exklusive Sperre für
Referenz-Typen. Wert-Typen werden nicht gesperrt. Wenn ein Wert-Typ
gesperrt werden muss, muss ein Referenz-Typ erstellt werden und dieser gesperrt werden
ReaderWriterLockSlim -
Lese/Schreibe-Sperre nach dem Flag-Prinzip (Sperre gilt nicht auf
Resourcen)
Interlocked - exklusive Sperre auf Int32 und Int64
Optimieren von sperren auf Collections
Beim
synchronisierten Zugriff auf Collections bieten viele Collections den
eingebauten Mechanismus diese in einen gesperrten Zustand zu versetzen.
Ein lock(ICollection.SyncRoot) bietet diese Möglichkeit für einen
optimierten Zugriff. ICollection.IsSyncronized gibt den Zustand der
Collection wieder.
Monitor vs. Lock
In C# wird für die Implementierung der Monitor Sperre ein
spezielles Schlüsselwort - lock - bereitgestellt. Dieses bildet intern
ein Monitor.Enter(lockObject) ab. Sie sind nach dem Compilerlauf
identisch, es wird der selbe IL Code erzeugt.
ReaderWriterLockSlim
Mit der
ReaderWriterLockSlim Klasse können ebenfalls die Zugriffe von
verschiedenen Threads auf eine gemeinsame Resource abgesichert werden.
Mehrere Threads können hierbei Lesezugriff oder einen exklusiven
Schreibzugriff erhalten. Leser und Schreiber werden bei diese Methode
der Sperre separiert gequeued. Ein Schreiber erhält einen exklusiven
Zugriff auf die Resource solange keine Leser eine Sperre auf das Objekt
hat. Hier wird abwechselnd zwischen den Lesern und einem Schreiber
gewechselt.
Auf fertige Thread
warten
Sollte auf die Abarbeitung eines einzigen Thread
gewarted werden müssen, sollte ein Aufruf der Instanzmethode Join auf
dem Thread-Objekt ausreichen. Wenn auf mehrere Threads gewarted werden
muss, bevor die weitere Abarbeitung erfolgt, bringt die statische Methode
WaitHandle.WaitAll mit der Übergabe eines WaitHandle-Objekt-Arrays die
gewünschte Hilfe mit. Mit der Instanzmethode WaitOne() von
Thread-Objekten kann auf den signalisierten Zustand eines Thread aus
einem anderen Thread gewartet werden. Ein Timeout kann hier ebenfalls
Deadlocks bzw. Race Conditions verhindern.
WaitHandle Klassen - Kommunikation
über Signalzustände zwischen Threads
Die WaitHandle-Klassen stellen
einen weiteren Synchronisierungsmechanismus dar. Hier wird über ein
Signal (signalisierter Zustand über Set()-Methode) bzw. ein
Systemsynchronisierungsereigniss der aktuelle Zustand des Handles dem
Aufrufer mitgeteilt. Es gibt zwei WaitHandles AutoResetEvent und
ManualResetEvent. Mit dem AutoResetEvent wird der Thread nach seiner
Abarbeitung freigegeben und das Signal automatisch zurückgesetzt. Beim
ManualResetEvent muss das Signal mit Reset() manuell zurückgesetzt
werden (unsignalisierter Zustand), um den Thread freizugeben.