SyncLock 语句SyncLock Statement

在执行块之前获取语句块的排他锁。Acquires an exclusive lock for a statement block before executing the block.

语法Syntax

SyncLock lockobject  
    [ block ]  
End SyncLock  

部件Parts

lockobject
必需。Required. 计算结果为对象引用的表达式。Expression that evaluates to an object reference.

block
可选。Optional. 获取锁时要执行的语句块。Block of statements that are to execute when the lock is acquired.

End SyncLock
终止 SyncLock 块。Terminates a SyncLock block.

备注Remarks

SyncLock 语句可确保多个线程不会同时执行语句块。The SyncLock statement ensures that multiple threads do not execute the statement block at the same time. SyncLock 阻止每个线程进入块,直到没有其他线程执行它。SyncLock prevents each thread from entering the block until no other thread is executing it.

SyncLock 的最常见用途是防止多个线程同时更新数据。The most common use of SyncLock is to protect data from being updated by more than one thread simultaneously. 如果操作数据的语句必须在不中断的情况下完成,请将它们放在 SyncLock 块内。If the statements that manipulate the data must go to completion without interruption, put them inside a SyncLock block.

受排他锁保护的语句块有时称为临界区A statement block protected by an exclusive lock is sometimes called a critical section.

规则Rules

  • 产生.Branching. 不能从块外分支到 SyncLock 块。You cannot branch into a SyncLock block from outside the block.

  • 锁定对象值。Lock Object Value. 不能 Nothing``lockobject 的值。The value of lockobject cannot be Nothing. SyncLock 语句中使用该锁对象之前,必须先创建它。You must create the lock object before you use it in a SyncLock statement.

    执行 SyncLock 块时,不能更改 lockobject 的值。You cannot change the value of lockobject while executing a SyncLock block. 机制要求锁定对象保持不变。The mechanism requires that the lock object remain unchanged.

  • 不能在 SyncLock 块中使用Await运算符。You can't use the Await operator in a SyncLock block.

行为Behavior

  • 机制.Mechanism. 当线程到达 SyncLock 语句时,它将计算 lockobject 表达式并挂起执行,直到它获取表达式返回的对象上的排他锁。When a thread reaches the SyncLock statement, it evaluates the lockobject expression and suspends execution until it acquires an exclusive lock on the object returned by the expression. 当另一个线程到达 SyncLock 语句时,在第一个线程执行 End SyncLock 语句之前,不会获取锁。When another thread reaches the SyncLock statement, it does not acquire a lock until the first thread executes the End SyncLock statement.

  • 受保护的数据。Protected Data. 如果 lockobjectShared 变量,则排他锁可防止类的任何实例中的线程执行 SyncLock 块,而任何其他线程正在执行该线程。If lockobject is a Shared variable, the exclusive lock prevents a thread in any instance of the class from executing the SyncLock block while any other thread is executing it. 这会保护在所有实例之间共享的数据。This protects data that is shared among all the instances.

    如果 lockobject 是实例变量(不 Shared),则锁定会阻止在当前实例中运行的线程与同一实例中的另一个线程同时执行 SyncLock 块。If lockobject is an instance variable (not Shared), the lock prevents a thread running in the current instance from executing the SyncLock block at the same time as another thread in the same instance. 这将保护由单个实例维护的数据。This protects data maintained by the individual instance.

  • 获取和发布。Acquisition and Release. SyncLock 块的行为类似于 Try...Finally 构造,其中 Try 块在 lockobject 上获取排他锁,而 Finally 块释放它。A SyncLock block behaves like a Try...Finally construction in which the Try block acquires an exclusive lock on lockobject and the Finally block releases it. 因此,无论退出块的方式如何,SyncLock 块均可保证锁的版本。Because of this, the SyncLock block guarantees release of the lock, no matter how you exit the block. 即使在发生未经处理的异常时也是如此。This is true even in the case of an unhandled exception.

  • 框架调用。Framework Calls. SyncLock 块通过调用 System.Threading 命名空间中的 Monitor 类的 EnterExit 方法获取并释放排他锁。The SyncLock block acquires and releases the exclusive lock by calling the Enter and Exit methods of the Monitor class in the System.Threading namespace.

编程做法Programming Practices

lockobject 表达式应始终计算为仅属于您的类的对象。The lockobject expression should always evaluate to an object that belongs exclusively to your class. 你应声明一个 Private 对象变量来保护属于当前实例的数据,或声明一个 Private Shared 对象变量来保护所有实例通用的数据。You should declare a Private object variable to protect data belonging to the current instance, or a Private Shared object variable to protect data common to all instances.

不应使用 Me 关键字为实例数据提供锁定对象。You should not use the Me keyword to provide a lock object for instance data. 如果类的外部代码具有对类的实例的引用,则可以使用该引用作为 SyncLock 块完全不同的锁对象,从而保护不同的数据。If code external to your class has a reference to an instance of your class, it could use that reference as a lock object for a SyncLock block completely different from yours, protecting different data. 通过这种方式,类和其他类可以相互阻止执行其不相关的 SyncLock 块。In this way, your class and the other class could block each other from executing their unrelated SyncLock blocks. 类似地锁定字符串可能会产生问题,因为使用相同字符串的进程中的任何其他代码都将共享同一个锁。Similarly locking on a string can be problematic since any other code in the process using the same string will share the same lock.

还应不要使用 Me.GetType 方法为共享数据提供锁定对象。You should also not use the Me.GetType method to provide a lock object for shared data. 这是因为 GetType 始终针对给定类名称返回相同的 Type 对象。This is because GetType always returns the same Type object for a given class name. 外部代码可以对类调用 GetType,并获取正在使用的同一个锁对象。External code could call GetType on your class and obtain the same lock object you are using. 这会导致两个类从其 SyncLock 块彼此阻止。This would result in the two classes blocking each other from their SyncLock blocks.

示例Examples

说明Description

下面的示例演示维护简单消息列表的类。The following example shows a class that maintains a simple list of messages. 它将消息保存在一个数组中,并将该数组的最后一个使用元素保存在一个变量中。It holds the messages in an array and the last used element of that array in a variable. addAnotherMessage 过程将递增最后一个元素并存储新消息。The addAnotherMessage procedure increments the last element and stores the new message. 这两个操作受 SyncLockEnd SyncLock 语句保护,因为当最后一个元素递增后,必须存储新消息,任何其他线程才能再次递增最后一个元素。Those two operations are protected by the SyncLock and End SyncLock statements, because once the last element has been incremented, the new message must be stored before any other thread can increment the last element again.

如果 simpleMessageList 类在其所有实例中共享一个消息列表,则 messagesListmessagesLast 的变量将被声明为 SharedIf the simpleMessageList class shared one list of messages among all its instances, the variables messagesList and messagesLast would be declared as Shared. 在这种情况下,还应 Shared变量 messagesLock,以便每个实例使用单个锁对象。In this case, the variable messagesLock should also be Shared, so that there would be a single lock object used by every instance.

代码Code

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

说明Description

下面的示例使用线程并 SyncLockThe following example uses threads and SyncLock. 只要存在 SyncLock 语句,语句块就会成为关键部分,并且 balance 绝不会成为负数。As long as the SyncLock statement is present, the statement block is a critical section and balance never becomes a negative number. 您可以注释掉 SyncLockEnd SyncLock 语句,以查看退出 SyncLock 关键字的效果。You can comment out the SyncLock and End SyncLock statements to see the effect of leaving out the SyncLock keyword.

代码Code

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

CommentsComments

另请参阅See also