Поделиться через


Оператор SyncLock

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

SyncLock lockobject
    [ block ]
End SyncLock

Части

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

  • block
    Необязательный параметр. Блок операторов, которые должны выполняться при запросе блокировки.

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

Заметки

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

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

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

Правила

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

  • Блокировка значения объекта. lockobject не может принимать значение Nothing. Необходимо создать объект блокировки, прежде чем использовать его в операторе SyncLock.

    Не удается изменить значение lockobject во время выполнения блока SyncLock. Механизм требует, чтобы заблокированный объект оставался неизменным.

  • Нельзя использовать оператор подождите в блоке SyncLock.

Поведение

  • Механизм. Когда поток достигает оператора SyncLock, он вычисляет выражение lockobject и приостанавливает выполнение до тех пор, пока не будет получена монопольная блокировка объекта, возвращенного выражением. Если другой поток достигает оператора SyncLock, он не получает блокировку до тех пор, пока первый поток не выполнит оператор End SyncLock.

  • Защита данных. Если lockobject является переменной Shared, при монопольной блокировке запрещается выполнение блока SyncLock, пока он выполняется любым другим потоком в любом экземпляре класса. Это обеспечивает защиту данных, являющихся общими для всех экземпляров.

    Если lockobject является переменной экземпляра (не Shared), при блокировке предотвращается выполнение потока в текущем экземпляре и не допускается выполнение блока SyncLock одновременно с другим потоком того же экземпляра. Это обеспечивает защиту данных, содержащихся в отдельном экземпляре.

  • Получение и освобождение. Блок SyncLock работает подобно конструкции Try...Finally, в которой блок Try получает эксклюзивную блокировку lockobject, а блок Finally освобождает его. В результате блок SyncLock гарантирует освобождение от блокировки независимо от способа выхода из блока. Это справедливо даже в случае необработанного исключения.

  • Вызов .NET Framework. Блок 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

См. также

Ссылки

System.Threading

Monitor

Синхронизация потоков (C# и Visual Basic)

Другие ресурсы

Работа с потоками (C# и Visual Basic)