我有一个包含Dictionary
的类,当调用LoadFromDb
方法时,该类从数据库加载,可以从任意数量的线程调用多次。
然后我有一个Employees
属性,可以被任何线程调用。
作为一个例子:
Public Class Employees
Private _employeesDictionary As New SortedDictionary(Of Int32, Employee)
Public ReadOnly Property Employees As IList(Of Employee)
Get
Return _employeesDictionary.Values.ToList
End Get
End Property
Public Sub loadFromDb()
Static rnd As New Random
_employeesDictionary = New SortedDictionary(Of Int32, Employee)
'generate a random number of employees
Dim numEmpsToAdd = rnd.Next(101)
For i = 0 To numEmpsToAdd
_employeesDictionary.Add(i + 1, New Employee With {.ClockNo = i + 1, .Name = $"Name:{i + 1}"})
Next
End Sub
End Class
我想确保LoadFromDb
只能在任何时候调用一次。如果有锁定,其他呼叫应该中止。
但是我想在此时锁定Employees属性,但是当没有调用LoadFromDb
时,我想允许多个线程读取值。
这基本上是解决方案,但我想知道如何使用SyncLock
,Mutex
,Semaphore
或其他可确保线程安全的方法来实现此目标。我最初尝试了SyncLock
,但是当没有调用Employees
时,这会阻止多个线程访问LoadFromDb
属性:
Public Class Employees
Private _employeesDictionary As New SortedDictionary(Of Int32, Employee)
Private locked As Boolean
Public ReadOnly Property Employees As IList(Of Employee)
Get
Do While locked
'wait for lock to be released
Loop
Return _employeesDictionary.Values.ToList
End Get
End Property
Public Sub loadFromDb()
Static rnd As New Random
If locked Then Exit Sub
locked = True
Try
_employeesDictionary = New SortedDictionary(Of Int32, Employee)
'generate a random number of employees
Dim numEmpsToAdd = rnd.Next(101)
For i = 0 To numEmpsToAdd
_employeesDictionary.Add(i + 1, New Employee With {.ClockNo = i + 1, .Name = $"Name:{i + 1}"})
Next
Catch ex As Exception
Throw ex
Finally
locked = False
End Try
End Sub
End Class
答案 0 :(得分:2)
使用Threading.Monitor方法可能是最好的方法。没有明显的原因进行测试。您知道由于rnd.Next的调用方式,有可能生成零记录。
Public Class Employees
Private _employeesDictionary As New SortedDictionary(Of Int32, Employee)
Private locked As New Object
Public ReadOnly Property Employees As IList(Of Employee)
Get
Dim rv As New List(Of Employee)
Threading.Monitor.Enter(locked)
'wait for lock to be released
rv = _employeesDictionary.Values.ToList
Threading.Monitor.Exit(locked)
Return rv
End Get
End Property
Private Shared rnd As New Random 'only one needed
Public Sub loadFromDb()
Try
If Threading.Monitor.TryEnter(locked) Then
_employeesDictionary = New SortedDictionary(Of Int32, Employee)
'generate a random number of employees
Dim numEmpsToAdd As Integer = rnd.Next(101)
For i As Integer = 0 To numEmpsToAdd
_employeesDictionary.Add(i + 1, New Employee With {.ClockNo = i + 1, .Name = $"Name:{i + 1}"})
Next
Threading.Monitor.Exit(locked)
End If
Catch ex As Exception
Throw ex
Finally
Threading.Monitor.Exit(locked)
End Try
End Sub
End Class
编辑:
Public Class Test
Private _employeesDictionary As New SortedDictionary(Of Int32, Int32)
Public ReadOnly Property Employees As IList(Of Int32)
Get
Dim rv As New List(Of Int32)
'wait for a Semaphore to be released
mySemaPhore.WaitOne()
rv = _employeesDictionary.Values.ToList
mySemaPhore.Release()
Return rv
End Get
End Property
Const maxThreads As Integer = 4
Private mySemaPhore As New Threading.Semaphore(maxThreads, maxThreads)
Private locked As New Object
Private Shared rnd As New Random 'only one needed
Public Sub loadFromDb()
Try
Dim tempDict As New SortedDictionary(Of Int32, Int32)
'generate a random number of employees
Dim numEmpsToAdd As Integer = rnd.Next(6)
For i As Integer = 0 To numEmpsToAdd
tempDict.Add(i + 1, i + 1)
Next
If Threading.Monitor.TryEnter(locked) Then
'get all semaphores
For x As Integer = 1 To maxThreads
mySemaPhore.WaitOne()
Next
_employeesDictionary = New SortedDictionary(Of Int32, Int32)(tempDict)
'release all semaphores
For x As Integer = 1 To maxThreads
mySemaPhore.Release()
Next
Threading.Monitor.Exit(locked)
End If
tempDict.Clear()
Catch ex As Exception
Throw ex
Finally
If Threading.Monitor.IsEntered(locked) Then
Threading.Monitor.Exit(locked)
End If
End Try
End Sub
End Class