5.2 Zusammenspiel 

Nachdem wir einige Threads zum Laufen gebracht haben, schauen wir uns an, was mit einem Thread noch passieren kann. Es gibt es drei wesentliche Zustände für einen aktiven Thread:
- laufend (running): wird gerade im Prozessor ausgeführt
- bereit (ready): in der Warteschlange des Prozessors
- wartend (waiting): inaktiv, bis er von einem anderen Prozess geweckt wird
Nur während ein Thread im ersten Zustand ist, kann er Programmcode ausführen. Da er mit anderen Threads in Konkurrenz steht, dauert dieser Zustand nicht ewig. Für die Aufgabe dieses Zustands gibt es nur wenige Gründe:
- Ein Thread höherer Priorität wird in der Warteschlange gestellt, woraufhin der Thread in die Warteschlange gestellt wird und der Thread mit höherer Priorität zur Ausführung kommt.
- Die vom Betriebssystem zugewiesene Zeit ist abgelaufen, und der Thread wird mit dem Zustand bereit in die Warteschlange gestellt.
- Der Thread gibt mit Sleep() sofort den Zustand freiwillig auf und geht nach einer Wartezeit in den Zustand bereit über.
- Der Thread muss auf einen anderen warten, ruft die Methode Join() des anderen Threads auf und tritt in den Zustand wartend ein.
Ihr Programm ist nie das einzige, das ausgeführt wird (ebenso wie man nie der Erste auf der Autobahn ist). Daher ist die Zusammenarbeit mit anderen Threads selbst dann wichtig, wenn Ihr Programm nur einen davon verwendet. Die folgenden Abschnitte beschreiben die Aspekte der Zusammenarbeit von Threads.
5.2.1 Priorität 

Die Rangfolge der Aktivitäten auf dem Rechner ist von entscheidender Bedeutung. Liefen alle Programme völlig gleichberechtigt, würden Benutzereingaben zur Qual: Jede einzelne Berechnung würde die Maus derart ausbremsen, dass ein vernünftiges Arbeiten unmöglich wird. Daher bekommen einige Anwendungen, wie zum Beispiel die Steuerung der Maus und der Tastatur, eine höhere Priorität zugewiesen und werden damit vor allen anderen niederer Priorität ausgeführt. Auf der anderen Seite gibt es nachrangige Aufgaben, wie zum Beispiel die Aufräumarbeiten des Garbage-Collector-Threads, der sich um die Speicherbereinigung kümmert. Erst, wenn die Ressourcen knapp werden, erhält er eine höhere Priorität zugewiesen, damit er dann in jedem Fall zum Zuge kommen kann.
Sie können Threads in Ihren Programmen auch explizit eine Priorität zuweisen. Dazu weisen Sie der Eigenschaft Priority Ihres Threads einen der Werte der Enumeration ThreadPriority zu: Lowest, BelowNormal, Normal, AboveNormal, Highest. Auch wenn Windows 31 Stufen der Priorität kennt, beschränken sich .NET-Programme auf diese fünf Werte. Wenn Sie diese einfach für alle Ihre Programme auf einen hohen Wert setzen, bremsen Sie alle anderen Programme aus. Daher gilt für alle Threads:
Threads hoher Priorität dürfen nur das Nötigste in möglichst kurzer Zeit erledigen. |
Auf der anderen Seite darf die Priorität auch nicht zu niedrig gewählt werden, denn:
Nur Threads im Zustand bereit mit der höchsten Priorität kommen zur Ausführung. |
Das folgende Codefragment zeigt, wie ein Thread höherer Priorität einen mit normaler Priorität in der Ausführung behindert. Dazu startet der Hauptthread mit normaler Priorität eine Schleife. Nach einigen Durchläufen wird ein Thread mit höherer Priorität gestartet. Der Test, ob der Thread noch nicht gestartet wurde (Unstarted), verhindert einen erneuten Startversuch.
'...\ Multitasking\Zusammenspiel\Priorität.vb |
Option Strict On
Imports System.Threading
Namespace Multitasking
Module Priorität
Sub Druck(ByVal z As Object)
For no As Integer = 0 To 1000
Console.Write(z)
If no = 5 AndAlso einheit.ThreadState = ThreadState.Unstarted Then _
einheit.Start("P")
Next
End Sub
Dim einheit As New Thread(AddressOf Druck)
Sub Test()
einheit.Priority = ThreadPriority.AboveNormal
Druck("N")
Console.ReadLine()
End Sub
End Module
End Namespace
Die Ausgabe zeigt einige wenige Ausgaben »N« des Threads mit normaler Priorität, bevor dann alle Ausgaben »P« des Threads höherer Priorität erfolgen. Dass es mehr als fünf »N« sind, liegt an der Zeit, die zum Starten eines Threads benötigt wird. Erst nach dem Ende des höher priorisierten Threads kommt der Thread mit normaler Priorität wieder zum Zuge.
NNNNNNNNNPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
...
PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
...
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
5.2.2 Warten mit Sleep 

Wenn eine Aktivität zu schnell ist, kann sie mit der Methode Sleep() ausgebremst werden. Dies kann zum Beispiel sinnvoll sein, um einen anderen Thread zum Zuge kommen zu lassen, damit Sie mit dem dann laufenden Thread kommunizieren können. Die Ruhezeit können Sie in Millisekunden oder als Zeitspanne angeben. Eine unendlich lange Wartezeit wird durch den speziellen Wert Timeout.Infinite repräsentiert (wie Sie da wieder herauskommen, zeigt der nächste Unterabschnitt).
Public Shared Sub Sleep(millisecondsTimeout As Integer) |
Bitte beachten Sie, dass die Methode mit Shared an die Klasse Thread gebunden ist. Sie können daher nicht einen beliebigen Thread warten lassen, sondern nur der gerade laufende Thread kann sich selbst zur Ruhe begeben. Alles andere könnte den Programmablauf nachhaltig stören, wenn eine Aktivität zur Unzeit unterbrochen wird. So behält jeder Thread selbst die Kontrolle darüber, ob er eine Zeit aussetzt. Die Methode Sleep() arbeitet in zwei Schritten:
- Freigabe des Prozessors
- Einreihung in die Warteschlange nach der angegebenen Zeit
Das folgende Beispiel zeigt, dass es notwendig sein kann, einen Thread ruhen zu lassen. Beide Threads, der Hauptthread und ein explizit gestarteter, sollen abwechselnd Zahlen auf der Konsole mit der Methode Druck() ausgeben. Dazu werden beide Methoden hintereinander durch Start(2) und Druck(1) gestartet. Ohne den Aufruf von Sleep() in der Methode Druck() ist sehr wahrscheinlich die durch Start(2) erzeugte Ausgabe so schnell, dass Druck(1) erst zum Zuge kommt, wenn bereits alle Zweien ausgegeben worden sind. Der Aufruf Sleep(0) gibt den Prozessor frei und stellt den Thread gleich wieder in die Warteschlange, da die Wartezeit null ist.
'...\ Multitasking\Zusammenspiel\Schlafen.vb |
Option Strict On
Imports System.Threading
Namespace Multitasking
Module Schlafen
Sub Druck(ByVal z As Object)
For no As Integer = 0 To 10
Thread.Sleep(0)
Console.Write(z)
Next
End Sub
Sub Test()
Dim zweiter As New Thread(AddressOf Druck)
zweiter.Start(2)
Druck(1)
Console.ReadLine()
End Sub
End Module
End Namespace
Die Ausgabe der Zahlen erfolgt abwechselnd.
1212121212121212121212
5.2.3 Unterbrechen mit Interrupt 

Ein Thread kann sich im Zustand wartend befinden, weil die Wartezeit eines Aufrufs von Sleep() noch nicht abgelaufen ist oder der Thread mit Join() den Prozessor für eine unbestimmte Zeit geräumt hat. Den zweiten Fall zeigt der übernächste Unterabschnitt. Durch welche der beiden Möglichkeiten der Thread in den Zustand wartend gekommen ist, spielt für die Beendigung des Zustandes keine Rolle. Der normale Programmablauf ist in diesem Zustand unmöglich. Der einzige Ausweg ist eine Ausnahme. Deshalb bietet die Klasse Thread die Methode Interrupt() an, um eine Ausnahme vom Typ ThreadInterruptedException auszulösen.
Public Sub Interrupt() |
Wie bei jeder anderen Ausnahme auch, können Sie mit einem Try/Catch-Block die Ausnahme abfangen. Im nächsten Codefragment wird die Methode Arbeiter() in einem neuen Thread gestartet. Innerhalb der Methode legt sich der neue Thread mit Sleep() für »immer« schlafen. Die einzige Möglichkeit, Arbeiter() zu wecken, besteht in dem Aufruf von Interrupt() für den Thread arb von Arbeiter(). Dadurch wird die Ausnahme ausgelöst und im Catch-Zweig aufgefangen.
'...\ Multitasking\Zusammenspiel\Interrupt.vb |
Option Strict On
Imports System.Threading
Namespace Multitasking
Module Unterbrechnung
Sub Arbeiter(ByVal o As Object)
Try
Thread.Sleep(Timeout.Infinite)
Console.WriteLine("Endlich ausgeschlafen.")
Catch ex As ThreadInterruptedException
Console.WriteLine("Siesta unterbrochen.")
End Try
End Sub
Sub Test()
Dim arb As New Thread(AddressOf Arbeiter)
arb.Start()
arb.Interrupt()
Console.ReadLine()
End Sub
End Module
End Namespace
Das Fehlen der Ausgabe "Endlich ausgeschlafen." zeigt, dass die Ausnahme, wie jede andere Ausnahme auch, den Code nach der Stelle der Ausnahmeauslösung, hier während Sleep(), überspringt.
Siesta unterbrochen.
Hinweis |
Befindet sich ein Thread beim Aufruf von Interrupt() nicht im Zustand wartend, wird die Unterbrechung ausgeführt, wenn er das nächste Mal den Zustand erreicht. |
5.2.4 Abbruch mit Abort 

Wenn ein Thread für eine Aufgabe zu lange braucht, kann es sinnvoll sein, ihn von außen abzubrechen. Dazu bietet die Klasse Thread zwei Methoden an. Analog zu Interrupt() lösen sie eine Ausnahme vom Typ ThreadAbortException in dem Thread aus, mit dessen Referenz Abort() aufgerufen wurde. Anders als bei der Unterbrechung mit Interrupt() kann der Thread nicht nur im Zustand wartend sein, sondern auch im Zustand laufend oder bereit. Während Sie bei Interrupt() die Stelle der Ausnahmeauslösung eingrenzen konnten (durch Aufruf von Sleep() oder Join()), ist dies bei Abort() nicht möglich.
Public Sub Abort() |
Sie sollten diese Möglichkeit der Terminierung nur im Notfall einsetzen, denn beim Aufruf wissen Sie nicht, was der abzubrechende Thread gerade macht. Schreibt er zum Beispiel gerade auf die Festplatte, können die geschriebenen Daten verloren sein. Wenn andererseits ein Thread auf ein nicht angeschlossenes Netzwerk zugreifen will, kann eine unbedingte Beendigung sinnvoll sein.
Hinweis |
Wenn der Abbruch während einer Klasseninitialisierung passiert, kann es sein, dass andere Klassen nicht geladen werden können. Sie sollten diese Situation unbedingt vermeiden. Eine andere kritische Situation ergibt sich, wenn im Finally-Zweig der ThreadAbortException-Ausnahme sehr umfangreiche Anweisungen stehen. Dann bricht der Thread de facto nicht ab. |
Das folgende Beispiel führt in einem eigenen Thread die Methode Rede()aus, die eine Ausgabe im Konsolenfenster erzeugt. Sie wird durch Sleep(1) ausgebremst, damit sie nicht fertig ist, bevor der Hauptthread einen Abbruch auslösen kann. Nach einer Pause von 200 Millisekunden wird der Thread mit Abort("Vorsitzender") zum Beenden aufgefordert. Dies löst in der Methode Rede() eine Ausnahme vom Typ ThreadAbortException aus, die in einem Catch-Zweig aufgefangen wird. Die dortige Ausgabe greift auf die Eigenschaft ExceptionState der Ausnahme zu, die den in Abort() übergebenen Parameter enthält. In einem Finally-Zweig wird noch eine Ausgabe erzeugt. Am Ende der Methode soll eine weitere Ausgabe erfolgen.
'...\ Multitasking\Zusammenspiel\Abbruch.vb |
Option Strict On
Imports System.Threading
Namespace Multitasking
Module Abbruch
Sub Rede()
Try
For Each c As Char In "Um zum Ende meiner Rede zu kommen ..."
Console.Write(c) : Thread.Sleep(1)
Next
Catch ex As ThreadAbortException
Console.WriteLine("{0}Abgebrochen durch: {1}!", _
Environment.NewLine, ex.ExceptionState)
Finally
Console.WriteLine("(war wohl nix)")
End Try
Console.WriteLine("Ich lade Sie ein, mir zu folgen und ...")
End Sub
Sub Test()
Dim politiker As New Thread(AddressOf Rede)
politiker.Start()
Thread.Sleep(200)
politiker.Abort("Vorsitzender")
Thread.Sleep(200)
Console.WriteLine("Redner redet weiter: {0}", politiker.IsAlive)
Console.ReadLine()
End Sub
End Module
End Namespace
Die erste Zeile der Ausgabe zeigt, dass die Ausgabe abgebrochen wurde. Die Ausgaben in den Catch- und Finally-Zweigen zeigen die zweite und dritte Zeile. Die letzte Zeile bestätigt, dass der Thread tatsächlich beendet wurde.
Um zum Ende meine
Abgebrochen durch: Vorsitzender!
(war wohl nix)
Redner redet weiter: False
Fehlt hier nicht die Ausgabe am Ende der Methode Rede()? Die Ausnahme haben wir ja abgefangen, und das Programm sollte nach dem Finally-Zweig fortgesetzt werden. Dies zeigt die besondere Stellung der Ausnahme ThreadAbortException. Ohne weitere Maßnahmen wird sie nämlich automatisch erneut ausgelöst. Wollen Sie dies verhindern und sich damit der drohenden Terminierung widersetzen, bietet die Klasse Thread die Methode ResetAbort() an.
Public Shared Sub ResetAbort() |
Wie die Methode Sleep() ist auch diese Methode mit Shared an die Klasse gebunden und wirkt sich nur auf den gerade aktiven Thread aus. Dazu zeigt das folgende Beispiel einen solchen unterbundenen Abbruch. Der Unterschied zum vorigen Beispiel besteht in dem Aufruf ResetAbort() im Catch-Zweig, der eine erneute Auslösung der Ausnahme verhindert, und in dem unbedingten Sprung mit GoTo zum Methodenanfang, um die Methode nach einem Abbruchversuch noch einmal zu durchlaufen. Obwohl der Sprung unschön ist, lässt er sich hier kaum vermeiden. Zum Beispiel kann ein Try/Catch-Block in einer While-Schleife scheitern, wenn der Abbruch genau dann passiert, wenn die Bedingung im Schleifenkopf ausgeführt wird, da sich dieser außerhalb des Fehlerbehandlungsblocks befindet. Dies zeigt, dass das korrekte Abfangen einer Abbruchanforderung nicht trivial ist und im Programmdesign frühzeitig berücksichtigt werden sollte.
'...\ Multitasking\Zusammenspiel\Abbruchversuch.vb |
Option Strict On
Imports System.Threading
Namespace Multitasking
Module Abbruchversuch
Sub Rede()
neu: Try
For Each c As Char In "Um zum Ende meiner Rede zu kommen ..."
Console.Write(c) : Thread.Sleep(1)
Next
Catch ex As ThreadAbortException
Console.WriteLine(Environment.NewLine & "(also nochmal)")
Thread.ResetAbort()
GoTo neu
End Try
Console.WriteLine(Environment.NewLine & "(fertig)")
End Sub
Sub Test()
Dim vorsitzender As New Thread(AddressOf Rede)
vorsitzender.Start()
Thread.Sleep(200)
vorsitzender.Abort()
Console.ReadLine()
End Sub
End Module
End Namespace
Im Gegensatz zum vorigen Beispiel wird die Methode nach der Abbruchanforderung neu gestartet und auch die Anweisungen nach dem Try/Catch-Block ausgeführt. Die Ausnahme ThreadAbortException ist also nicht erneut ausgelöst worden.
Um zum Ende m
(also nochmal)
Um zum Ende meiner Rede zu kommen ...
(fertig)
Hinweis |
Ist ein Thread beim Abbruch noch nicht gestartet, wird er beim Start abgebrochen. Ist er im Zustand wartend, wird er vor dem Abbruch ohne Auslösung der Ausnahme ThreadInterruptedException unterbrochen. |
5.2.5 Warten mit Join 

Wenn ein Thread sicherstellen muss, dass erst ein anderer Thread eine Aufgabe erledigt hat, bevor er selbst mit der Programmausführung fortfährt, kann er sich selbst in den Zustand wartend versetzen, indem er sich an den anderen Thread »dranhängt«. Dazu ruft er die Methode Join() des Threads auf, auf den er warten will. Der Rückgabewert ist True, wenn der andere Thread innerhalb der Zeitspanne fertig wurde.
Public Sub Join() |
Damit der wartende Thread weiterarbeiten kann, muss eine der folgenden Bedingungen erfüllt sein, die wir uns im Folgenden ansehen:
- Der andere Thread ist beendet.
- Die angegebene Zeitspanne ist abgelaufen.
- Der andere Thread erweckt den wartenden Thread mit Interrupt() wieder zum Leben.
Hinweis |
Warten zwei Threads jeweils aufeinander, können sie beide nicht weiterlaufen. Man nennt dies einen Deadlock. Nur ein dritter Thread kann das Patt auflösen. Gibt es ihn nicht, hat sich das Programm »aufgehängt«. |
Das folgende Codefragment zeigt den einfachsten Fall, dass ein Thread einfach wartet, bis der andere beendet ist.
'...\ Multitasking\Zusammenspiel\Warten.vb |
Option Strict On
Imports System.Threading
Namespace Multitasking
Module Warten
Sub Bad()
Thread.Sleep(1000)
End Sub
Sub Test()
Dim morgens As New Thread(AddressOf Bad)
morgens.Start()
Console.Write("Warten von {0} ", Now)
morgens.Join()
Console.WriteLine("bis {0} (fertig: {1})", Now, Not morgens.IsAlive)
Console.ReadLine()
End Sub
End Module
End Namespace
Zwischen den beiden Zeitangaben liegt (mindestens) die in Sleep() angegebene Zeit. Die Ausgabe zeigt auch, dass der andere Thread vor der Ausgabe beendet worden ist.
Warten von 9/18/2008 2:35:04 PM bis 9/18/2008 2:35:05 PM (fertig: True)
In vielen Situationen kann es sinnvoll sein, nur eine begrenzte Zeit auf den anderen Thread zu warten. Dazu wird im nächsten Beispiel, das den Ladevorgang mehrerer Dateien simuliert, eine maximale Wartezeit von 500 Millisekunden an die Methode Join() übergeben. Durch den Aufruf von Sleep() in der Methode Laden() ist der andere Thread nach dieser Zeit noch nicht fertig, und der Hauptthread fährt nach dem Aufruf von Join() fort. In dem Beispiel bricht Abort() den anderen Thread ab. Dieser gibt im Catch-Zweig den in Abort() angegebenen Parameter aus.
'...\ Multitasking\Zusammenspiel\Zeitüberschreitung.vb |
Option Strict On
Imports System.Threading
Namespace Multitasking
Module Zeitüberschreitung
Sub Laden()
Try
Console.Write("Lade ")
For no As Integer = 0 To 10
Thread.Sleep(100)
Console.Write("Datei {0} ", no)
Next
Catch ex As ThreadAbortException
Console.WriteLine(ex.ExceptionState.ToString())
End Try
End Sub
Sub Test()
Dim arb As New Thread(AddressOf Laden)
arb.Start()
Console.WriteLine("Warten auf das Ende.")
Dim fertig As Boolean = arb.Join(500)
arb.Abort("Schluss jetzt.")
Console.WriteLine("Genug gewartet (fertig: {0}).", fertig)
Console.ReadLine()
End Sub
End Module
End Namespace
Wie erwartet, werden weniger als 11 Dateien ausgegeben, und der Rückgabewert von Join() ist False. Ob die Ausgabe mitten im Wort abgebrochen wird und in welcher Reihenfolge die beiden Texte "Genug gewartet." und "Schluss jetzt." erscheinen, hängt vom exakten Zeitpunkt ab, zu dem der Abbruch den Thread erwischt. Wollen Sie hier etwas mehr Sicherheit, können Sie nach dem Abort die Methode Sleep() aufrufen. Ein Konstrukt zur exakten Steuerung der Reihenfolge zeigt das nächste Beispiel.
Warten auf das Ende.
Lade Datei 0 Datei 1 Datei 2 Datei 3 Genug gewartet (fertig: False).
Schluss jetzt.
In der Praxis werden Ihre wartenden Threads oft nicht auf das endgültige Ende eines anderen Threads warten, sondern darauf, dass dieser eine Teilaufgabe sicher erledigt hat. Auch in diesem Fall versetzen Sie einen Thread mittels Join() in den Zustand wartend. Aber anstatt das Ende des anderen Threads abzuwarten, weckt dieser den wartenden Thread durch den Aufruf von Interrupt() (siehe auch Abschnitt 5.2.3, »Unterbrechen mit Interrupt«).
Im folgenden Beispiel wird die Methode Suchen() in einem neuen Thread gestartet. Um die Methode allgemein zu halten, werden ihr alle nötigen Informationen mitgegeben: den suchenden Thread und die zu durchsuchende Zeichenkette. Da die Methode Start() in der Klasse Thread nur ein Objekt vom Typ Object als Übergabeparameter erlaubt, verpacken wir die Information in einem Array mit zwei Objekten. Durch diese Beschränkung sind am Anfang der Methode Suchen() ein paar Typumwandlungen nötig. Sowie in Suchen() ein »e« gefunden wurde, wird der wartende Thread durch Interrupt() geweckt. Die If-Bedingung stellt sicher, dass der Thread nur einmal geweckt wird. Um sicherzustellen, dass nun der Hauptthread in Aktion tritt, bevor der zweite Thread fortfährt, wird dieser durch Join() in den Zustand wartend versetzt. Durch den Interrupt() vor dem Join() in Suchen() wird im Hauptthread die Ausnahme ThreadInterruptedException ausgelöst. Im Catch-Zweig der Methode Test() wird eine Ausgabe erzeugt und der zweite Thread seinerseits mit Interrupt() zum Leben erweckt, sodass dieser mit der Suche fortfahren kann. Im ganzen Programm kommt nicht ein einziger Aufruf von Sleep() vor.
'...\ Multitasking\Zusammenspiel\Ungeduld.vb |
Option Strict On
Imports System.Threading
Namespace Multitasking
Module Ungeduld
Sub Suchen(ByVal data As Object)
Dim thrd As Thread = CType(CType(data, Object())(0), Thread)
Dim text As String = CType(CType(data, Object())(1), String)
For Each c As Char In text
If c = "e"c Then
If thrd.ThreadState = ThreadState.WaitSleepJoin Then
thrd.Interrupt()
Try
thrd.Join()
Catch ex As ThreadInterruptedException
End Try
End If
Console.WriteLine(c)
End If
Next
End Sub
Sub Test()
Dim textsuche As New Thread(AddressOf Suchen)
textsuche.Start(New Object() {Thread.CurrentThread, _
"Ein Text mit ein paar ""e""."})
Try
textsuche.Join()
Catch ex As ThreadInterruptedException
Console.WriteLine("Erstes ""e"" gefunden.")
textsuche.Interrupt()
End Try
Console.ReadLine()
End Sub
End Module
End Namespace
Durch dieses etwas komplexe Wechselspiel verschiedener Aufrufe von Join() und Interrupt() ist sichergestellt, dass die Meldung "Erstes ""e"" gefunden." garantiert vor den Ausgaben der Suchmethode erfolgt. Die einfachere Variante, Aufrufe von Sleep() zu verwenden, könnte unter unglücklichen Umständen diese Garantie nicht geben.
Erstes "e" gefunden.
e
e
e
5.2.6 Warten erzwingen mit Suspend 

Es gibt extrem selten die Notwendigkeit, einen anderen Thread zum Warten zu zwingen. Die hierfür notwendige Methode Suspend() ist als obsolet gekennzeichnet und sollte nur im Notfall eingesetzt werden. Stattdessen sollten Sie die in Abschnitt 5.3, »Gesicherter Datenaustausch«, beschriebenen Klassen Monitor, Mutex, Event und Semaphore verwenden.
Public Sub Suspend() |
Der so angehaltene Thread kann mit Resume() wieder zum Leben erweckt werden:
Public Sub [Resume]() |
Das folgende Codefragment definiert eine Methode Report(), die in regelmäßigen Abständen den Wert der Variablen Anzahl ausgibt. Die Methode wird in einem neuen Thread gestartet. Im Hauptthread wird der Wert der Variablen Anzahl ein paar Mal geändert. Vorher wird der zweite Thread mit Suspend() in den Zustand wartend versetzt und danach mit Resume() wieder freigegeben. Schließlich wird der zweite Thread mit Abort() brutal beendet.
Hinweis |
Dieses Beispiel schlechter Threadkommunikation steht hier nur der Vollständigkeit halber. |
'...\ Multitasking\Zusammenspiel\Zwang.vb |
Option Strict On
Imports System.Threading
Namespace Multitasking
Module Zwang
Dim Anzahl As Integer
Sub Report()
While True
Console.Write(Anzahl & " ")
Thread.Sleep(10)
End While
End Sub
Sub Test()
Dim ausgabe As New Thread(AddressOf Report)
ausgabe.Start()
Thread.Sleep(100)
For no As Integer = 0 To 6
ausgabe.Suspend()
Thread.Sleep(20) 'Simulation umfangreicher Berechnungen
Anzahl = Now.Millisecond
ausgabe.Resume()
Thread.Sleep(0)
Next
ausgabe.Abort()
Console.ReadLine()
End Sub
End Module
End Namespace
Durch das Blockieren schafft es die Methode Report() bei den meisten Läufen nicht, die sieben von Null verschiedenen Ausgaben zu erzeugen.
0 0 0 0 0 0 0 0 0 0 934 934 964 4 4 34 74
5.2.7 Threadzustände 

Der Zustand eines Threads ist in der schreibgeschützten Eigenschaft ThreadState vom Typ der gleichnamigen Enumeration gespeichert. Die in Tabelle 5.1 aufgelisteten Werte sind Potenzen von 2 und können auch kombiniert werden (siehe Abschnitt 2.7.4, »Bitweise Operatoren«). Lediglich Running hat den Wert 0 und sollte gesondert behandelt werden. Wenn Sie robuste Programme mit mehreren Threads schreiben wollen, werden Sie über kurz oder lang alle diese Zustände berücksichtigen müssen.
Konstante | Beschreibung |
Running |
Laufend (weder blockiert noch angeforderter Abbruch) |
StopRequested |
Vorbereitung für das Ende (nur für internen Gebrauch) |
SuspendRequested |
Zustand direkt nach Suspend() |
Background |
Gibt an, ob der Thread im Hintergrund läuft. |
Unstarted |
Noch nicht gestartet. |
Stopped |
Komplett beendet. |
WaitSleepJoin |
Zustand während der Sleep()-Zeit sowie nach Join() |
Suspended |
Der Zustand wartend wurde erreicht. |
AbortRequested |
Der Zustand direkt nach Abort() |
Aborted |
Der Zustand nach Abort() ohne ResetAbort() |
5.2.8 Die Klasse Thread 

Thread kann nicht beerbt werden, das Verhalten liegt also endgültig fest. Tabelle 5.2 und Tabelle 5.3 listen die öffentlichen Mitglieder als Kurzreferenz. In der Auflistung sind kursiv gesetzte Parameter und Namensteile in eckigen Klammern optional. Um die Liste kurz zu halten, sind die von Object geerbten Mitglieder nicht aufgeführt, ebenso wie die obsoleten Mitglieder ApartmentState(), SetCompressedStack(), GetCompressedStack(), Suspend() und Resume().
Methode | Beschreibung | |
New(start As [Parameterized]ThreadStart, maxStackSize As Integer) |
Konstruktoren (starten den Thread nicht). |
|
BeginCriticalRegion() EndCriticalRegion() |
Threadabbrüche/unbehandelte Ausnahmen können andere Threads gefährden. |
S |
BeginThreadAffinity() EndThreadAffinity() |
Region, die darauf angewiesen ist, dass Aktivitäten nicht den Thread wechseln |
S |
MemoryBarrier() |
Speicherreorganisation nur vor/nach Aufruf erlaubt, nicht übergreifend. |
S |
GetDomain() As AppDomain GetDomainID() As Integer |
Anwendungsdomäne des aktuellen Threads |
S |
Start(parameter As Object) |
In den Zustand bereit wechseln |
|
SpinWait(iterations As Integer) |
Schleife für aktuellen Thread ausführen |
S |
Abort(stateInfo As Object) |
Zum Beenden auffordern |
|
ResetAbort() |
Abbruch des aktuellen Threads ablehnen |
S |
Join(msTimeout As Integer) As Boolean Join(timeout As TimeSpan) As Boolean |
Aktuellen Thread blockieren, bis der Join-Thread beendet wurde oder die gegebene optionale Zeit verstrichen ist |
|
Sleep(msTimeout As Integer) Sleep(timeout As TimeSpan) |
Aktuellen Thread für die angegebene Zeit aussetzen |
S |
Interrupt() |
ThreadInterruptedException in einem wartenden Thread auslösen; hebt die Blockade von Join() und Sleep() auf. |
|
GetApartmentState() As ApartmentState [Try]SetApartmentState( state As ApartmentState) As Boolean |
Einen oder mehrere Threads für COM-Anwendungen erlauben |
|
AllocateDS() As LS AllocateNamedDS(name As String) As LS GetNamedDS(name As String) As LS FreeNamedDS(name As String) GetData(slot As LS) As Object SetData(slot As LS, data As Object) |
Threadspezifische Datenfelder verwalten. GetData() und SetData() betreffen den aktuellen Thread, die anderen alle Threads. LS: LocalDataStoreSlot, DS: DataSlot |
S |
VolatileRead(ByRef address As <datum>) VolatileWrite(ByRef address As <datum>, value As Byte) |
Transfer unabhängig von Prozessorcaches. <datum>: alle primitiven Zahlentypen, IntPtr, UIntPtr und Object. |
S |
Hinweis |
Die Steuerung eines Threads mit Suspend() und Resume() sollten Sie unbedingt vermeiden, da Sie nicht wissen, was dieser gerade macht und dadurch sehr leicht andere Threads blockiert werden können. |
Mitglied | Beschreibung | |
CurrentThread() As Thread |
Gerade ausgeführter Thread |
RS |
CurrentContext() As Context |
Aktuelle Umgebung des aktuellen Threads (unter anderem für Sicherheitseinstellungen) |
RS |
CurrentPrincipal() As IPrincipal |
Aktueller Benutzer (rollenbasierte Sicherheit) |
S |
ManagedThreadId() As Integer |
Eindeutige Nummer für den Thread |
R |
ExecutionContext() As ExecutionContext |
Umgebungsinformation zur Interdomänenkommunikation (Sicherheit, Aufruf, …) |
R |
Priority() As ThreadPriority |
Ausführungspriorität |
|
IsAlive() As Boolean |
Gestartet und noch nicht terminiert/abgebrochen |
R |
IsThreadPoolThread() As Boolean |
Der Thread ist Teil eines Pools. |
R |
IsBackground() As Boolean |
Der Thread wird im Hintergrund ausgeführt. |
|
ThreadState() As ThreadState |
Zustand, unter anderem laufend, bereit, wartend |
R |
CurrentUICulture() As CultureInfo |
ID der zur Laufzeit geladenen Ressourcen |
|
CurrentCulture() As CultureInfo |
Länderinformation |
|
Name() As String |
Einmalig setzbarer Name |
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.