SyncLock
语句确保多个线程不会同时执行语句块。
SyncLock
会阻止每个线程进入语句块,直到没有其他线程正在执行它。
SyncLock
最常见的用途是防止多个线程同时更新数据。 如果操作数据的语句必须不间断地完成,请将它们放在
SyncLock
块中。
受排他锁保护的语句块有时称为关键部分。
分支。 不能从
SyncLock
块的外部分支到此块。
锁对象值。
lockobject
的值不能为
Nothing
。 必须先创建锁对象,然后才能在
SyncLock
语句中使用它。
执行
SyncLock
块时不能更改
lockobject
的值。 该机制要求锁对象保持不变。
不能在
SyncLock
块中使用
Await
运算符。
机制。 当线程到达
SyncLock
语句时,它会计算
lockobject
表达式并暂停执行,直到获取表达式返回的对象上的排他锁。 当另一个线程到达
SyncLock
语句时,在第一个线程执行
End SyncLock
语句之前,它不会获取锁。
受保护的数据。 如果
lockobject
是
Shared
变量,当该类的任何实例中的线程正在执行
SyncLock
块时,排他锁会阻止任何其他线程执行该块。 这可以保护在所有实例之间共享的数据。
如果
lockobject
是实例变量(而非
Shared
),排他锁会阻止当前实例中运行的线程与同一实例中的另一个线程同时执行
SyncLock
块。 这可以保护由单个实例维护的数据。
获取和释放。
SyncLock
块的行为类似于
Try...Finally
构造,其中
Try
块获取
lockobject
上的排他锁,
Finally
块将其释放。 因此,无论如何退出
SyncLock
块,它都会保证释放锁。 即使发生未经处理的异常,也是如此。
框架调用。
SyncLock
块通过调用
System.Threading
命名空间中
Monitor
类的
Enter
和
Exit
方法来获取和释放排他锁。
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
' 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))
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
For i As Integer = 0 To 9
threads(i).Start()
End Sub
End Module
System.Threading.Monitor
System.Threading.Interlocked
同步基元概述