SyncLock — Instrukcja
Uzyskuje wyłączną blokadę bloku instrukcji przed wykonaniem bloku.
Składnia
SyncLock lockobject
[ block ]
End SyncLock
generatora
lockobject
Wymagany. Wyrażenie, które oblicza odwołanie do obiektu.
block
Opcjonalny. Blok instrukcji, które mają być wykonywane po uzyskaniu blokady.
End SyncLock
SyncLock
Przerywa blok.
Uwagi
Instrukcja SyncLock
zapewnia, że wiele wątków nie wykonuje jednocześnie bloku instrukcji. SyncLock
uniemożliwia każdemu wątkowi wprowadzanie bloku do momentu, aż żaden inny wątek nie zostanie on wykonywany.
Najczęstszym zastosowaniem programu SyncLock
jest ochrona danych przed aktualizowaniem przez więcej niż jeden wątek jednocześnie. Jeśli instrukcje, które manipulują danymi, muszą przejść do zakończenia bez przerwy, umieść je w SyncLock
bloku.
Blok instrukcji chroniony przez blokadę wyłączną jest czasami nazywany sekcją krytyczną.
Reguły
Rozgałęzienia. Nie można rozgałęzić
SyncLock
bloku spoza bloku.Zablokuj wartość obiektu. Wartość nie może być
Nothing
wartościąlockobject
. Przed użyciem go w instrukcjiSyncLock
należy utworzyć obiekt lock.Nie można zmienić wartości
lockobject
podczas wykonywaniaSyncLock
bloku. Mechanizm wymaga, aby obiekt blokady pozostał niezmieniony.Nie można użyć operatora Await w
SyncLock
bloku.
Zachowanie
Mechanizm. Gdy wątek osiągnie
SyncLock
instrukcję, obliczalockobject
wyrażenie i zawiesza wykonywanie, dopóki nie uzyska wyłącznej blokady obiektu zwróconego przez wyrażenie. Gdy inny wątek osiągnie instrukcjęSyncLock
, nie uzyskuje blokady, dopóki pierwszy wątek nie wykona instrukcjiEnd SyncLock
.Chronione dane. Jeśli
lockobject
jest zmiennąShared
, blokada wyłączna uniemożliwia wątkowi w dowolnym wystąpieniu klasy wykonywanieSyncLock
bloku, podczas gdy jakikolwiek inny wątek go wykonuje. Chroni to dane, które są współużytkowane przez wszystkie wystąpienia.Jeśli
lockobject
jest zmienną wystąpienia (nieShared
), blokada uniemożliwia wątkowi uruchomionemu w bieżącym wystąpieniu wykonywanieSyncLock
bloku w tym samym czasie co inny wątek w tym samym wystąpieniu. Chroni to dane przechowywane przez pojedyncze wystąpienie.Pozyskiwanie i wydawanie. Blok
SyncLock
zachowuje się jakTry...Finally
konstrukcja, w którejTry
blok uzyskuje wyłączną blokadęlockobject
iFinally
blok go zwalnia. W związku z tymSyncLock
blok gwarantuje zwolnienie blokady, bez względu na sposób zamykania bloku. Jest to prawdą nawet w przypadku nieobsługiwanego wyjątku.Wywołania struktury. Blok
SyncLock
uzyskuje i zwalnia blokadę wyłączną przez wywołanieEnter
metodMonitor
iExit
klasy w System.Threading przestrzeni nazw.
Praktyki programistyczne
Wyrażenie lockobject
powinno zawsze oceniać obiekt należący wyłącznie do klasy. Należy zadeklarować zmienną Private
obiektu w celu ochrony danych należących do bieżącego wystąpienia lub Private Shared
zmiennej obiektu w celu ochrony danych wspólnych dla wszystkich wystąpień.
Nie należy używać słowa kluczowego Me
do udostępniania obiektu blokady dla danych wystąpień. Jeśli kod zewnętrzny klasy zawiera odwołanie do wystąpienia klasy, może użyć tego odwołania jako obiektu blokady dla SyncLock
bloku zupełnie innego niż twoje, chroniąc różne dane. W ten sposób klasa i druga klasa mogą blokować sobie wykonywanie niepowiązanych SyncLock
bloków. Podobnie blokowanie ciągu może być problematyczne, ponieważ każdy inny kod w procesie przy użyciu tego samego ciągu będzie współużytkować tę samą blokadę.
Nie należy również używać Me.GetType
metody w celu udostępnienia obiektu blokady dla udostępnionych danych. Jest to spowodowane tym, że GetType
zawsze zwraca ten sam Type
obiekt dla danej nazwy klasy. Kod zewnętrzny może wywołać GetType
klasę i uzyskać ten sam obiekt blokady, którego używasz. Spowodowałoby to zablokowanie sobie dwóch klas z ich SyncLock
bloków.
Przykłady
opis
W poniższym przykładzie przedstawiono klasę, która utrzymuje prostą listę komunikatów. Przechowuje komunikaty w tablicy i ostatni używany element tej tablicy w zmiennej. Procedura addAnotherMessage
zwiększa ostatni element i przechowuje nowy komunikat. Te dwie operacje są chronione przez SyncLock
instrukcje i End SyncLock
, ponieważ po dokonaniu przyrostu ostatniego elementu nowy komunikat musi być przechowywany, zanim jakikolwiek inny wątek będzie mógł ponownie zwiększać ostatni element.
simpleMessageList
Jeśli klasa udostępniła jedną listę komunikatów między wszystkimi wystąpieniami, zmienne messagesList
i messagesLast
zostaną zadeklarowane jako Shared
. W takim przypadku zmienna powinna również mieć Shared
wartość messagesLock
, aby istniał pojedynczy obiekt blokady używany przez każde wystąpienie.
Kod
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
opis
W poniższym przykładzie użyto wątków i SyncLock
. Jeśli instrukcja SyncLock
jest obecna, blok instrukcji jest sekcją krytyczną i balance
nigdy nie staje się liczbą ujemną. Możesz skomentować instrukcje SyncLock
i End SyncLock
, aby zobaczyć efekt pomijania słowa kluczowego SyncLock
.
Kod
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