我应该在这段代码中使用什么 SyncLock?在哪里?
我有一个类,其中有两个方法,一个调用一个创建并执行多个线程的类,另一个是一个事件处理程序,用于处理这些线程完成时引发的事件(然后再次调用第一个方法)。
据我了解,处理事件的方法在引发事件的线程中运行。因此,我 SyncLock
是一个成员变量,它表示正在运行的线程数并从中减去一:
SyncLock Me ' GetType(me)
_availableThreads -= 1
End SyncLock
所以我有几个问题:
主要问题:我应该使用 SyncLock 'ing _availableThreads
在类中的任何地方 - 即在创建线程的方法中(创建线程时加 1)
与此问题相关的附带问题:
我通常
SyncLock
当前实例,但我见过SyncLock
是类型的代码,那么同步锁定Me
(当前实例)和同步锁定之间有什么区别GetType(Me)
?两者之间会有性能差异吗? 任何较小的东西我可以锁定上面的内容而不影响其他任何东西 - 也许是为了锁定类中的东西而创建的单独的“挂锁”对象?
注意:_availableThreads
的唯一目的是控制在任何给定时间可以运行的线程数量以及线程处理可能需要数小时才能运行的作业。
代码:
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
I have a class that has two method in it, one calls a class which creates and executes a number of threads, the other is an event handler that handles an event raised when those threads complete (and then calls the first method again).
I understand that the method that handles the event runs in the thread that raised the event. So as such, I SyncLock
a member variable that says how many threads are running and subtract one from it:
SyncLock Me ' GetType(me)
_availableThreads -= 1
End SyncLock
So I have a few questions:
Main Question: Should I be SyncLock'ing _availableThreads
everywhere in the class - i.e in the method that creates the threads (which adds 1 when a thread is created)
Side Questions related to this question:
I'd usually
SyncLock
the current instance, but I've seen code thatSyncLock
s the type instead, so what is the difference between sync lockingMe
(Current Instance) andGetType(Me)
?Would there be a performance difference between the two? and is there anything smaller I'd be able to lock for the above that doesn't affect anything else - perhaps a separate 'padlock' object created for the sole purpose of locking things within a class?
Note: The sole purpose of _availableThreads
is to control how many threads can run at any given time and the threads process jobs that can take hours to run.
Code:
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您不应该
SyncLock
实例或类型。您总是希望对完全在类控制范围内的变量进行 SyncLock,但两者都不是。您应该声明一个私有New Object
并将其用于您的SyncLock
。...
You shouldn't
SyncLock
the instance or the type. You always want toSyncLock
on a variable that is fully within the control of the class, and neither of those are. You should declare a privateNew Object
and use that for yourSyncLock
....
不幸的是,您需要在这里做一些不同的事情。
首先,我建议避免使用 SyncLock,并使用 Interlocked.Increment 和 Interlocked.Decrement 来处理更改的 _availableThreads。这将为该变量提供线程安全性,而无需锁定。
话虽如此,如果从多个线程使用它,您仍然需要在每次访问队列时使用 SyncLock。如果您使用 .NET 4,另一种方法是改用新的 ConcurrentQueue(Of T) 类而不是队列。如果您使用SyncLock,您应该创建一个只能由您的类访问的私有对象,并将其用于所有同步。
Unfortunately, you need to do a few things differently here.
First off, I'd recommend avoiding SyncLock, and using Interlocked.Increment and Interlocked.Decrement to handle changing _availableThreads. This will provide thread safety for that variable without a lock.
That being said, you still will need a SyncLock around every access to your Queue - if it's being used from multiple threads. An alternative, if you're using .NET 4, would be to change over to using the new ConcurrentQueue(Of T) class instead of Queue. If you use SyncLock, you should create a private object only accessible by your class, and use it for all synchronization.
您应该在此处使用 Interlocked 类,即 Decrement() 方法来减少计数。是的,在任何地方都可以访问变量。
使用 SyncLock Me 与 SyncLock GetType(Me) 一样糟糕。您应该始终使用私有对象来锁定,这样就不会有人意外地导致死锁。黄金法则是您不能锁定数据,只能阻止代码访问数据。由于代码是您的私有实现细节,因此保存锁定状态的对象也必须是私有细节。您的对象(Me)和该对象的类型都不是私有的。允许其他代码意外锁定它。
You should be using the Interlocked class here, the Decrement() method to decrease the count. Yes, everywhere the variable is accessed.
Using SyncLock Me is as bad as SyncLock GetType(Me). You should always use a private object to lock on so nobody can accidentally cause a deadlock. The golden rule is that you cannot lock data, you can only block code from accessing data. Since the code is your private implementation detail, the object that holds the lock state must also be a private detail. Neither your object (Me) nor the Type of that object is private. Allowing other code to lock it by accident.
您可以用信号量代替线程计数器。如果您使用 Semaphore,则无需退出 while 循环,也无需从 ThreadCompleted 事件处理程序调用 WorkThroughQueue()。信号量是线程安全的,因此您可以在不锁定的情况下使用它。
http://www.albahari.com/threading/part2.aspx#_Semaphore
You can substitute the thread counter with Semaphore. If you use Semaphore you do not need to exit from while loop and neither it is necessary to call WorkThroughQueue() from ThreadCompleted event handler. Semaphore is thread safe so you can use it without locking.
http://www.albahari.com/threading/part2.aspx#_Semaphore