SyncLock-Anweisung

Ruft eine exklusive Sperre für einen Anweisungsblock ab, bevor der Block ausgeführt wird.

Syntax

SyncLock lockobject  
    [ block ]  
End SyncLock  

Bestandteile

lockobject
Erforderlich. Ausdruck, der einen Objektverweis auswertet.

block
Optional. Block von Anweisungen, die ausgeführt werden sollen, wenn die Sperre abgerufen wird.

End SyncLock
beendet einen SyncLock-Block.

Bemerkungen

Die SyncLock-Anweisung stellt sicher, dass nicht mehrere Threads gleichzeitig den Anweisungsblock ausführen. SyncLock sorgt dafür, dass ein Thread den Block erst dann startet, wenn ihn kein anderer Thread mehr ausführt.

SyncLock wird am häufigsten verwendet, um zu verhindern, dass Daten von mehreren Threads gleichzeitig aktualisiert werden. Wenn die Anweisungen, die die Daten bearbeiten, ohne Unterbrechung abgeschlossen werden müssen, platzieren Sie sie in einen SyncLock-Block.

Ein Anweisungsblock, der durch eine exklusive Sperre geschützt wird, wird manchmal als kritischer Abschnitt bezeichnet.

Regeln

  • Branchen. Eine Verzweigung in einen SyncLock-Block von außerhalb ist nicht möglich.

  • Sperrobjekt-Wert. Der Wert von lockobject kann nicht Nothing sein. Sie müssen das Sperrobjekt erstellen, bevor Sie es in einer SyncLock-Anweisung verwenden.

    Sie können den Wert von lockobject während der Ausführung eines SyncLock-Blocks nicht ändern. Der Mechanismus setzt voraus, dass das Sperrobjekt nicht geändert wird.

  • Sie können den Await-Operator nicht in einem SyncLock-Block verwenden.

Verhalten

  • Mechanismus. Wenn ein Thread die SyncLock-Anweisung erreicht, wertet er den lockobject-Ausdruck aus und setzt die Ausführung aus, bis er eine exklusive Sperre für das Objekt abgerufen hat, das vom Ausdruck zurückgegeben wird. Wenn ein weiterer Thread die SyncLock-Anweisung erreicht, ruft er erst eine Sperre ab, wenn der erste Thread die End SyncLock-Anweisung ausführt.

  • Geschützte Daten. Wenn lockobject eine Shared-Variable ist, verhindert die exklusive Sperre, dass ein Thread in einer Instanz der Klasse den SyncLock-Block ausführt, während er von einem anderen Thread ausgeführt wird. So werden Daten geschützt, die von allen Instanzen gemeinsam genutzt werden.

    Wenn lockobject eine Instanzvariable ist (nicht Shared), verhindert die Sperre, dass ein Thread, der in der aktuellen Instanz ausgeführt wird, den SyncLock-Block zur selben Zeit ausführt wie ein anderer Thread in derselben Instanz. So werden Daten geschützt, die von der einzelnen Instanz verwaltet werden.

  • Abruf und Freigabe. Ein SyncLock-Block verhält sich wie eine Try...Finally-Konstruktion, in der der Try-Block eine exklusive Sperre für lockobject abruft und der Finally-Block sie freigibt. Aus diesem Grund gewährleistet der SyncLock-Block die Freigabe der Sperre, unabhängig davon, wie Sie den Block verlassen. Das gilt selbst im Falle eines Ausnahmefehlers.

  • Framework-Aufrufe. Der SyncLock-Block ruft die exklusive Sperre ab und gibt sie wieder frei, indem er die Methoden Enter und Exit der Klasse Monitor im Namespace System.Threading aufruft.

Programmierverfahren

Der Ausdruck lockobject sollte immer zu einem Objekt ausgewertet werden, das exklusiv zu Ihrer Klasse gehört. Sie sollten eine Private-Objektvariable so deklarieren, dass die Daten, die zur aktuellen Instanz gehören, geschützt werden, und eine Private Shared-Objektvariable so, dass die Daten, die von allen Instanzen genutzt werden, geschützt werden.

Sie sollten das Schlüsselwort Me nicht dazu verwenden, ein Sperrobjekt für Instanzdaten bereitzustellen. Wenn Code außerhalb Ihrer Klasse auf eine Instanz Ihrer Klasse verweist, könnte er diesen Verweis als Sperrobjekt für einen SyncLock-Block verwenden, der völlig von Ihrem abweicht, sodass unterschiedliche Daten geschützt werden. So könnte es dazu kommen, dass Ihre Klasse und die andere Klasse sich gegenseitig davon abhalten, die nicht zusammenhängenden SyncLock-Blöcke auszuführen. Auch die Sperre für eine Zeichenfolge kann problematisch sein, da jeder andere Code im Prozess, der dieselbe Zeichenfolge verwendet, auch dieselbe Sperre verwendet.

Sie sollten die Me.GetType-Methode auch nicht dazu verwenden, ein Sperrobjekt für freigegebene Daten bereitzustellen. Grund dafür ist, dass GetType immer dasselbe Type-Objekt für einen Klassennamen zurückgibt. Externer Code könnte GetType in Ihrer Klasse aufrufen und dasselbe Sperrobjekt abrufen, das Sie gerade verwenden. Dies würde dazu führen, dass die zwei Klassen sich gegenseitig davon abhalten, ihre SyncLock-Blöcke auszuführen.

Beispiele

BESCHREIBUNG

Das folgende Beispiel zeigt eine Klasse, die eine einfache Liste an Nachrichten verwaltet. Die Nachrichten sind in einem Array enthalten, und das zuletzt verwendete Element dieses Arrays ist in einer Variablen enthalten. Die addAnotherMessage-Prozedur erhöht das letzte Element und speichert es in einer neuen Nachricht. Diese zwei Vorgänge werden durch die SyncLock- und End SyncLock-Anweisungen geschützt, da nach dem Erhöhen des letzten Elements die neue Nachricht gespeichert werden muss, bevor ein anderer Thread das letzte Element erneut erhöhen kann.

Falls die Klasse simpleMessageList eine Liste an Nachrichten für alle Instanzen freigegeben hat, würden die Variablen messagesList und messagesLast als Shared deklariert werden. In diesem Fall sollte die Variable messagesLock ebenfalls Shared sein, damit es ein einziges Sperrobjekt gibt, das von jeder Instanz verwendet wird.

Code

Class simpleMessageList
    Public messagesList() As String = New String(50) {}
    Public messagesLast As Integer = -1
    Private messagesLock As New Object
    Public Sub addAnotherMessage(ByVal newMessage As String)
        SyncLock messagesLock
            messagesLast += 1
            If messagesLast < messagesList.Length Then
                messagesList(messagesLast) = newMessage
            End If
        End SyncLock
    End Sub
End Class

BESCHREIBUNG

Das folgende Beispiel verwendet Threads und SyncLock. Solange die SyncLock-Anweisung vorhanden ist, ist der Anweisungsblock ein kritischer Abschnitt und balance wird niemals eine negative Zahl. Sie können die SyncLock- und End SyncLock-Anweisungen auskommentieren, um zu testen, welche Auswirkung das Weglassen des Schlüsselworts SyncLock hat.

Code

Imports System.Threading

Module Module1

    Class Account
        Dim thisLock As New Object
        Dim balance As Integer

        Dim r As New Random()

        Public Sub New(ByVal initial As Integer)
            balance = initial
        End Sub

        Public Function Withdraw(ByVal amount As Integer) As Integer
            ' This condition will never be true unless the SyncLock statement
            ' is commented out:
            If balance < 0 Then
                Throw New Exception("Negative Balance")
            End If

            ' Comment out the SyncLock and End SyncLock lines to see
            ' the effect of leaving out the SyncLock keyword.
            SyncLock thisLock
                If balance >= amount Then
                    Console.WriteLine("Balance before Withdrawal :  " & balance)
                    Console.WriteLine("Amount to Withdraw        : -" & amount)
                    balance = balance - amount
                    Console.WriteLine("Balance after Withdrawal  :  " & balance)
                    Return amount
                Else
                    ' Transaction rejected.
                    Return 0
                End If
            End SyncLock
        End Function

        Public Sub DoTransactions()
            For i As Integer = 0 To 99
                Withdraw(r.Next(1, 100))
            Next
        End Sub
    End Class

    Sub Main()
        Dim threads(10) As Thread
        Dim acc As New Account(1000)

        For i As Integer = 0 To 9
            Dim t As New Thread(New ThreadStart(AddressOf acc.DoTransactions))
            threads(i) = t
        Next

        For i As Integer = 0 To 9
            threads(i).Start()
        Next
    End Sub

End Module

Kommentare

Weitere Informationen