Оператор SyncLock

Получает монопольную блокировку для блока операторов перед выполнением блока.

Синтаксис

SyncLock lockobject  
    [ block ]  
End SyncLock  

Компоненты

lockobject
Обязательный элемент. Выражение, результатом которого является ссылка на объект.

block
Необязательный элемент. Блок инструкций, которые выполняются при получении блокировки.

End SyncLock
Завершает SyncLock блок.

Комментарии

SyncLockИнструкция гарантирует, что несколько потоков не выполняют блок инструкций одновременно. SyncLock предотвращает вход каждого потока в блок до тех пор, пока не будет выполнен другой поток.

Чаще всего используется SyncLock для защиты данных от одновременного обновления более чем одним потоком. Если инструкции, управляющие данными, должны переходить к завершению без прерывания, помещайте их внутрь SyncLock блока.

Блок операторов, защищенный монопольной блокировкой, иногда называют критическим разделом.

Правила

  • Ветвления. Нельзя выполнить ветвление в SyncLock блок, находящийся за пределами блока.

  • Блокировка значения объекта. Значение lockobject не может быть Nothing . Объект Lock необходимо создать до его использования в 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 блоков.

Примеры

Описание

В следующем примере показан класс, который поддерживает простой список сообщений. Он хранит сообщения в массиве и последнем используемом элементе этого массива в переменной. addAnotherMessageПроцедура увеличивает последний элемент и сохраняет новое сообщение. Эти две операции защищаются SyncLock End SyncLock инструкциями и, так как после увеличения последнего элемента новое сообщение должно быть сохранено до того, как любой другой поток снова сможет снова увеличить последний элемент.

Если simpleMessageList класс поделился одним списком сообщений между всеми его экземплярами, переменные messagesList и будут messagesLast объявлены как 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

Комментарии

См. также