Erweiterte Synchronisierungsverfahren
Aktualisiert: November 2007
Multithreadanwendungen verwenden häufig Wait-Handles und Monitor-Objekte, um mehrere Threads zu synchronisieren. In diesen Abschnitten wird die Verwendung der folgenden .NET Framework-Klassen beim Synchronisieren von Threads erläutert: AutoResetEvent, Interlocked, ManualResetEvent, Monitor, Mutex, ReaderWriterLock, Timer und WaitHandle.
Wait-Handles
Wait-Handles sind Objekte, die den Status eines Threads einem anderen Thread signalisieren. Threads können mithilfe von Wait-Handles andere Threads darüber informieren, dass sie exklusiven Zugriff auf eine Ressource benötigen. Andere Threads müssen in diesem Fall mit der Verwendung der Ressource warten, bis das Wait-Handle nicht mehr verwendet wird. Wait-Handles haben zwei Statuswerte: signalisiert und nicht signalisiert. Ein Wait-Handle, das nicht im Besitz von einem Thread ist, hat den Status signalisiert. Ein Wait-Handle, das im Besitz von einem Thread ist, hat den Status nicht signalisiert.
Threads fordern den Besitz an einem wait-Handle an, indem sie eine der Wartemethoden aufrufen, zum Beispiel WaitOne, WaitAny oder WaitAll. Die wait-Methoden sind blockierende Aufrufe, ähnlich wie die Join-Methode eines einzelnen Threads:
Wenn kein anderer Thread im Besitz des wait-Handles ist, gibt der Aufruf sofort True zurück, der Status des wait-Handles ändert sich auf nicht signalisiert, und der Thread, der das wait-Handle besitzt, wird weiter ausgeführt.
Wenn ein Thread eine der wait-Methoden des wait-Handles aufruft, aber ein anderer Thread im Besitz des wait-Handles ist, wartet der aufrufende Thread entweder eine bestimmte Zeit (wenn ein Timeout angegeben ist) oder unbegrenzt lange (wenn kein Timeout angegeben ist), bis der andere Thread das wait-Handle freigibt. Wenn ein Timeout angegeben ist und das wait-Handle vor Ablauf des Timeouts freigegeben wird, gibt der Aufruf True zurück. Andernfalls gibt der Aufruf False zurück, und der aufrufende Thread wird weiter ausgeführt.
Threads, die ein Wait-Handle besitzen, rufen die Set-Methode auf, wenn sie beendet sind oder das Wait-Handle nicht mehr benötigen. Andere Threads können den Status eines wait-Handles auf nicht signalisiert zurücksetzen, indem sie entweder die Reset-Methode oder WaitOne, WaitAny oder WaitAll aufrufen und erfolgreich darauf warten, dass ein Thread Set aufruft. AutoResetEvent-Handles werden vom System automatisch nach Freigabe eines einzelnen wartenden Threads zurückgesetzt. Wenn sich keine Threads in Warteposition befinden, verbleibt das Ereignisobjekt im signalisierten Zustand.
Im Allgemeinen werden drei Arten von wait-Handles mit Visual Basic verwendet: Mutexobjekte, ManualResetEvent und AutoResetEvent. Die letzteren beiden werden auch häufig als Synchronisierungsereignisse bezeichnet.
Mutex-Objekte
Mutex-Objekte sind Synchronisierungsobjekte, die immer nur im Besitz eines einzelnen Threads sein können. Der Name "Mutex" leitet sich aus der Tatsache ab, dass der Besitz an Mutex-Objekten sich gegenseitig ausschließt (engl. "MUTually EXclusive"). Ein Thread fordert den Besitz an einem Mutex-Objekt an, wenn er exklusiven Zugriff auf eine Ressource benötigt. Da zu einer Zeit immer nur ein Thread ein Mutex-Objekt besitzen kann, müssen andere Threads warten, bis sie im Besitz eines Mutex-Objekts sind, um die betreffende Ressource verwenden zu können.
Die WaitOne-Methode veranlasst einen aufrufenden Thread, auf den Besitz eines Mutex-Objekts zu warten. Wird ein Thread normal beendet, während er im Besitz eines Mutex-Objekts ist, wird der Status des Mutex-Objekts auf signalisiert gesetzt, und der nächste wartende Thread wird Besitzer.
Synchronisierungsereignisse
Mit Synchronisierungsereignissen werden andere Threads darüber informiert, dass ein bestimmtes Ereignis eingetreten oder eine Ressource verfügbar ist. Obwohl in diesem Begriff das Wort "Ereignis" vorkommt, unterscheiden sich Synchronisierungsereignisse von anderen Visual Basic-Ereignissen, da es sich hierbei eigentlich um wait-Handles handelt. Wie andere Wait-Handles haben Synchronisierungsereignisse zwei Statuswerte: signalisiert und nicht signalisiert.
Threads, die eine der Wait-Methoden eines Synchronisierungsereignisses aufrufen, müssen warten, bis ein anderer Thread das Ereignis durch Aufrufen der Set-Methode signalisiert. Es gibt zwei Synchronisierungsereignisklassen: ManualResetEvent und AutoResetEvent.
Threads legen den Status von ManualResetEvent-Instanzen mithilfe der Set-Methode auf signalisiert fest. Der Status von ManualResetEvent-Instanzen wird von Threads mithilfe der Reset-Methode oder durch Rückgabe der Steuerung an einen wartenden WaitOne-Aufruf auf nicht signalisiert festgelegt.
Instanzen der AutoResetEvent-Klasse können ebenfalls mit der Set-Methode auf signalisiert festgelegt werden, allerdings kehren sie zum Status nicht signalisiert zurück, sobald ein wartender Thread darüber informiert wird, dass das Ereignis signalisiert wurde.
Monitor-Objekte und SyncLock
Monitor-Objekte stellen sicher, dass ein Codeblock ausgeführt wird, ohne durch Code, der in anderen Threads ausgeführt wird, unterbrochen zu werden. Anders gesagt: Code in anderen Threads kann erst ausgeführt werden, nachdem der Code im synchronisierten Codeblock beendet ist.
Beispiel: Angenommen, Sie verwenden ein Programm, das Daten wiederholt und asynchron liest und die Ergebnisse anzeigt. Bei Betriebssystemen, die präemptives Multitasking verwenden, kann ein Thread, der gerade ausgeführt wird, durch das Betriebssystem unterbrochen werden, damit Zeit für die Ausführung eines anderen Threads ist. Ohne Synchronisierung erhalten Sie möglicherweise eine teilweise aktualisierte Ansicht der Daten, wenn das Objekt, das die Daten darstellt, durch einen anderen Thread geändert wird, während die Daten angezeigt werden. Mit Monitor-Objekten kann sichergestellt werden, dass ein bestimmter Codeabschnitt ohne Unterbrechungen ausgeführt wird. Visual Basic bietet zur Vereinfachung des Zugriffs auf Monitor-Objekte die SyncLock-Anweisung und die End SyncLock-Anweisung. Visual C# verwendet das Lock-Schlüsselwort auf dieselbe Weise.
Hinweis: |
---|
Der Zugriff auf ein Objekt wird nur dann gesperrt, wenn der Zugriffscode in einem SyncLock-Block in der gleichen Objektinstanz enthalten ist. |
Weitere Informationen zur SyncLock-Anweisung finden Sie unter SyncLock-Anweisung.
Interlocked-Klasse
Mit den Methoden der Interlocked-Klasse können Sie Probleme verhindern, die auftreten können, wenn mehrere Threads gleichzeitig versuchen, den gleichen Wert zu aktualisieren oder zu vergleichen. Mit den Methoden dieser Klasse können Sie Werte aus beliebigen Threads auf sichere Weise schrittweise erhöhen oder verringern, austauschen und vergleichen. Das folgende Beispiel zeigt, wie Sie mit der Increment-Methode eine Variable schrittweise erhöhen, die von Prozeduren, die in separaten Threads ausgeführt werden, gemeinsam genutzt wird.
Sub ThreadA(ByRef IntA As Integer)
System.Threading.Interlocked.Increment(IntA)
End Sub
Sub ThreadB(ByRef IntA As Integer)
System.Threading.Interlocked.Increment(IntA)
End Sub
ReaderWriter-Sperren
In manchen Fällen soll eine Ressource vielleicht nur gesperrt werden, wenn Daten geschrieben werden, und mehrere Clients sollen gleichzeitig Daten lesen können, wenn Daten nicht aktualisiert werden. Die ReaderWriterLock-Klasse gewährleistet exklusiven Zugriff auf eine Ressource, während ein Thread die Ressource ändert, erlaubt jedoch einen nicht exklusiven Zugriff beim Lesen der Ressource. ReaderWriter-Sperren sind eine nützliche Alternative zu exklusiven Sperren, die andere Threads in den Wartezustand zwingen, selbst wenn diese gar keine Daten aktualisieren müssen.
Deadlocks
Auch wenn die Threadsynchronisierung in Multithreadanwendungen sehr nützlich ist, besteht immer die Gefahr eines deadlock, bei dem mehrere Threads aufeinander warten und die Anwendung dadurch in eine Endlosschleife gerät. Deadlocks können mit der Situation an einer Kreuzung verglichen werden, bei der vier Autos darauf warten, dass eines losfährt. Die Vermeidung von Deadlocks ist wichtig und erfordert sorgfältige Planung. Oft können Sie Deadlocks vorhersagen, indem Sie ein Diagramm einer Multithreadanwendung erstellen, bevor Sie mit der Codierung beginnen.
Siehe auch
Konzepte
Erweitertes Multithreading mit Visual Basic