Оператор SyncLock
Получает монопольную блокировку для блока инструкций перед выполнением блока инструкций.
Синтаксис
SyncLock lockobject
[ block ]
End SyncLock
Детали
lockobject
Обязательный. Выражение, которое вычисляет ссылку на объект.
block
Необязательно. Блок инструкций, выполняемых при получении блокировки.
End SyncLock
SyncLock
Завершает блок.
Замечания
Инструкция SyncLock
гарантирует, что одновременно несколько потоков не выполняют блок инструкций. SyncLock
Запрещает каждому потоку вводить блок до тех пор, пока не выполняется ни один другой поток.
Наиболее распространенным способом является SyncLock
защита данных от обновления несколькими потоками одновременно. Если операторы, которые управляют данными, должны завершиться без прерывания, поместите их в SyncLock
блок.
Блок инструкций, защищенный монопольной блокировкой, иногда называется критически важным разделом.
Правила
Ветвления. Невозможно ветвь в блок за пределами
SyncLock
блока.Значение объекта lock. Значение
lockobject
не может бытьNothing
. Перед использованием объекта блокировки необходимо создать его в инструкцииSyncLock
.Невозможно изменить значение
lockobject
при выполненииSyncLock
блока. Механизм требует, чтобы объект блокировки оставался неизменным.Оператор Await нельзя использовать в блоке
SyncLock
.
Поведение
Механизм. Когда поток достигает
SyncLock
инструкции, он оцениваетlockobject
выражение и приостанавливает выполнение, пока не получит монопольную блокировку объекта, возвращаемого выражением. Когда другой поток достигает инструкцииSyncLock
, он не получает блокировку, пока первый поток не выполнит инструкциюEnd SyncLock
.Защищенные данные. Если
lockobject
этоShared
переменная, монопольная блокировка предотвращает выполнение потока в любом экземпляре классаSyncLock
во время выполнения любого другого потока. Это защищает данные, общие для всех экземпляров.Если
lockobject
это переменная экземпляра (неShared
), блокировка предотвращает выполнение потока в текущем экземпляреSyncLock
одновременно с другим потоком в том же экземпляре. Это защищает данные, поддерживаемые отдельным экземпляром.Приобретение и выпуск. Блок
SyncLock
ведет себя какTry...Finally
конструкция, в которойTry
блок получает монопольную блокировкуlockobject
иFinally
блок освобождает его. Из-за этого блок гарантирует освобождение блокировки независимо от того,SyncLock
как вы выходите из блока. Это верно даже в случае необработанного исключения.Вызовы платформы. Блок
SyncLock
получает и освобождает монопольную блокировку путем вызоваEnter
иExit
методовMonitor
класса в System.Threading пространстве имен.
Методики программирования
Выражение lockobject
всегда должно оценивать объект, принадлежащий исключительно вашему классу. Следует объявить Private
переменную объекта для защиты данных, принадлежащих текущему экземпляру, или Private Shared
переменной объекта для защиты данных, общих для всех экземпляров.
Не следует использовать Me
ключевое слово для предоставления объекта блокировки для данных экземпляра. Если код, внешний для вашего класса, имеет ссылку на экземпляр класса, он может использовать такую ссылку в качестве объекта блокировки для SyncLock
блока совершенно отличается от вашего, защищая разные данные. Таким образом, класс и другой класс могут блокировать друг друга от выполнения несвязанных SyncLock
блоков. Аналогичным образом блокировка строки может быть проблематичной, так как любой другой код в процессе, используя ту же строку, будет совместно использовать ту же блокировку.
Метод также не следует использовать Me.GetType
для предоставления объекта блокировки для общих данных. Это связано с тем, что GetType
всегда возвращает один и тот же Type
объект для заданного имени класса. Внешний код может вызывать GetType
класс и получать тот же объект блокировки, который вы используете. Это приведет к тому, что два класса блокируют друг друга из своих SyncLock
блоков.
Примеры
Description
В следующем примере показан класс, который поддерживает простой список сообщений. Он содержит сообщения в массиве и последний используемый элемент этого массива в переменной. Процедура addAnotherMessage
увеличивает последний элемент и сохраняет новое сообщение. Эти две операции защищены SyncLock
и End SyncLock
операторы, так как после увеличения последнего элемента новое сообщение должно храниться, прежде чем любой другой поток может снова увеличить последний элемент.
simpleMessageList
Если класс поделился одним списком сообщений среди всех его экземпляров, переменные и messagesLast
будут объявлены messagesList
как Shared
. В этом случае переменная messagesLock
также должна быть Shared
, чтобы существовал один объект блокировки, используемый каждым экземпляром.
Код
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
Описание
В следующем примере используются потоки и SyncLock
. Если SyncLock
оператор присутствует, блок инструкции является критически важным разделом и balance
никогда не становится отрицательным числом. Вы можете закомментировать и SyncLock
End SyncLock
закомментировать инструкции, чтобы увидеть эффект выхода SyncLock
из ключевое слово.
Код
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