SyncLock 语句

在执行语句块之前获取该块的排他锁。

语法

SyncLock lockobject  
    [ block ]  
End SyncLock  

组成部分

lockobject
必需。 计算结果为对象引用的表达式。

block
可选。 获取锁时要执行的语句块。

End SyncLock
终止 SyncLock 块。

注解

SyncLock 语句确保多个线程不会同时执行语句块。 SyncLock 会阻止每个线程进入语句块,直到没有其他线程正在执行它。

SyncLock 最常见的用途是防止多个线程同时更新数据。 如果操作数据的语句必须不间断地完成,请将它们放在 SyncLock 块中。

受排他锁保护的语句块有时称为关键部分。

规则

  • 分支。 不能从 SyncLock 块的外部分支到此块。

  • 锁对象值。 lockobject 的值不能为 Nothing。 必须先创建锁对象,然后才能在 SyncLock 语句中使用它。

    执行 SyncLock 块时不能更改 lockobject 的值。 该机制要求锁对象保持不变。

  • 不能在 SyncLock 块中使用 Await 运算符。

行为

  • 机制。 当线程到达 SyncLock 语句时,它会计算 lockobject 表达式并暂停执行,直到获取表达式返回的对象上的排他锁。 当另一个线程到达 SyncLock 语句时,在第一个线程执行 End SyncLock 语句之前,它不会获取锁。

  • 受保护的数据。 如果 lockobjectShared 变量,当该类的任何实例中的线程正在执行 SyncLock 块时,排他锁会阻止任何其他线程执行该块。 这可以保护在所有实例之间共享的数据。

    如果 lockobject 是实例变量(而非 Shared),排他锁会阻止当前实例中运行的线程与同一实例中的另一个线程同时执行 SyncLock 块。 这可以保护由单个实例维护的数据。

  • 获取和释放。 SyncLock 块的行为类似于 Try...Finally 构造,其中 Try 块获取 lockobject 上的排他锁,Finally 块将其释放。 因此,无论如何退出 SyncLock 块,它都会保证释放锁。 即使发生未经处理的异常,也是如此。

  • 框架调用。 SyncLock 块通过调用 System.Threading 命名空间中 Monitor 类的 EnterExit 方法来获取和释放排他锁。

编程做法

lockobject 表达式应始终计算为专属于你的类的对象。 你应该声明一个 Private 对象变量来保护属于当前实例的数据,或者声明一个 Private Shared 对象变量来保护所有实例共有的数据。

不应使用 Me 关键字为实例数据提供锁对象。 如果你的类外部的代码引用类实例,它可以将该引用用作 SyncLock 块(与你的块完全不同)的锁对象,从而保护不同的数据。 这样,你的类和另一个类可以阻止对方执行其不相关的 SyncLock 块。 以相似的方式锁定字符串可能会出现问题,因为进程中使用同一字符串的任何其他代码将共享同一个锁。

此外,不应使用 Me.GetType 方法为共享数据提供锁对象。 这是因为 GetType 始终为给定的类名返回相同的 Type 对象。 外部代码可以对你的类调用 GetType,并获取你正在使用的相同锁对象。 这将导致两个类阻止对方执行其 SyncLock 块。

示例

说明

以下示例展示了用于维护简单消息列表的类。 它将消息保存在数组中,并将该数组的最后一个已用元素保存在变量中。 addAnotherMessage 过程递增最后一个元素并存储新消息。 这两个操作受到 SyncLockEnd SyncLock 语句的保护,因为一旦递增最后一个元素,就必须先存储新消息,其他线程才能再次递增最后一个元素。

如果 simpleMessageList 类在其所有实例之间共享一个消息列表,变量 messagesListmessagesLast 将被声明为 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 永远不会是负数。 你可以注释掉 SyncLockEnd 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

注释

另请参阅