我有一个类中有两个方法,一个调用一个创建并执行多个线程的类,另一个是一个事件处理程序,它处理在这些线程完成时引发的事件(然后再调用第一个方法) )。
我知道处理事件的方法在引发事件的线程中运行。因此,我SyncLock一个成员变量,说明正在运行多少个线程并从中减去一个:
SyncLock Me //GetType(me)
_availableThreads -= 1
End SyncLock
所以我有几个问题:
主要问题:我是否应该在类中的任何地方使用SyncLock'ing_availableThreads - 即在创建线程的方法中(在创建线程时添加1)
与此问题相关的问题:
我通常会同步SyncLock当前实例,但我看到SyncLocks类型的代码,所以同步锁定Me
(当前实例)和GetType(me)
之间的区别是什么
两者之间会有性能差异吗?并且有什么小的我可以锁定以上不会影响其他任何东西 - 可能是一个单独的'挂锁'对象创建的唯一目的是锁定类中的东西?
注意:_available线程的唯一目的是控制在任何给定时间可以运行的线程数,并且线程处理可能需要数小时才能运行的作业。
代码:
Public Class QManager
Private _maxThreadCount, _availableThreads As Integer
Public Sub New(ByVal maxThreadCount As Integer)
Me.MaximumThreadCount = maxThreadCount
End Sub
Public Sub WorkThroughQueue()
//get jobs from queue (priorities change, so call this every time)
Dim jobQ As Queue(Of QdJobInfo) = QueueDAO.GetJobList
//loop job queue while there are jobs and we have threads available
While jobQ.Count > 0 And _availableThreads <= _maxThreadCount
//create threads for each queued job
Dim queuedJob As New QdJob(jobQ.Dequeue)
AddHandler queuedJob.ThreadComplete, AddressOf QueuedJob_ThreadCompleted
_availableThreads += 1 //use a thread up (do we need a sync lock here?)***************************
queuedJob.Process() //go process the job
End While
//when we get here, don't do anything else - when a job completes it will call this method again
End Sub
Private Sub QueuedJob_ThreadCompleted(ByVal sender As QdJobInfo, ByVal args As EventArgs)
SyncLock Me //GetType(me)
_availableThreads -= 1
End SyncLock
//regardless of how the job ended, we want to carry on going through the rest of the jobs
WorkThroughQueue()
End Sub
#Region "Properties"
Public Property MaximumThreadCount() As Integer
Get
Return _maxThreadCount
End Get
Set(ByVal value As Integer)
If value > Environment.ProcessorCount * 2 Then
_maxThreadCount = value
Else
value = Environment.ProcessorCount
End If
LogFacade.LogInfo(_logger, "Maximum Thread Count set to " & _maxThreadCount)
End Set
End Property
#End Region
End Class
答案 0 :(得分:6)
您不应该SyncLock
实例或类型。对于完全在类控制范围内的变量,您总是希望SyncLock
,而这些变量都不是。您应声明私人New Object
并将其用于SyncLock
。
Private lockObject as New Object()
...
SyncLock lockObject
...
End SyncLock
答案 1 :(得分:2)
你应该在这里使用Interlocked类,使用Decrement()方法来减少计数。是的,无处不在访问变量。
使用SyncLock Me与SyncLock GetType(Me)一样糟糕。您应该始终使用私有对象锁定,以便没有人可以意外地导致死锁。黄金法则是您无法锁定数据,您只能阻止代码访问数据。由于代码是您的私有实现细节,因此保存锁定状态的对象也必须是私有细节。您的对象(Me)和该对象的Type都不是私有的。允许其他代码意外锁定它。
答案 2 :(得分:2)
不幸的是,你需要在这里做一些不同的事情。
首先,我建议避免使用SyncLock,并使用Interlocked.Increment和Interlocked.Decrement来处理更改_availableThreads。这将为没有锁定的变量提供线程安全性。
话虽这么说,如果从多个线程使用它,你仍然需要围绕对Queue的每次访问都有一个SyncLock。如果您使用的是.NET 4,则可以选择使用新的ConcurrentQueue(Of T)类而不是Queue。如果使用SyncLock,则应创建只能由类访问的私有对象,并将其用于所有同步。
答案 3 :(得分:1)
您可以使用信号量替换线程计数器。如果使用Semaphore,则不需要从while循环退出,也不需要从ThreadCompleted事件处理程序调用WorkThroughQueue()。信号量是线程安全的,因此您可以在不锁定的情况下使用它。