我应该在这段代码中使用什么 SyncLock?在哪里?

发布于 2024-10-12 06:10:36 字数 2639 浏览 3 评论 0原文

我有一个类,其中有两个方法,一个调用一个创建并执行多个线程的类,另一个是一个事件处理程序,用于处理这些线程完成时引发的事件(然后再次调用第一个方法)。

据我了解,处理事件的方法在引发事件的线程中运行。因此,我 SyncLock 是一个成员变量,它表示正在运行的线程数并从中减去一:

SyncLock Me ' GetType(me)
    _availableThreads -= 1
End SyncLock

所以我有几个问题:

主要问题:我应该使用 SyncLock 'ing _availableThreads 在类中的任何地方 - 即在创建线程的方法中(创建线程时加 1)

与此问题相关的附带问题:

  1. 我通常 SyncLock 当前实例,但我见过 SyncLock 是类型的代码,那么同步锁定 Me (当前实例)和同步锁定之间有什么区别GetType(Me)

  2. 两者之间会有性能差异吗? 任何较小的东西我可以锁定上面的内容而不影响其他任何东西 - 也许是为了锁定类中的东西而创建的单独的“挂锁”对象?

注意:_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:

  1. I'd usually SyncLock the current instance, but I've seen code that SyncLocks the type instead, so what is the difference between sync locking Me (Current Instance) and GetType(Me)?

  2. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

待天淡蓝洁白时 2024-10-19 06:10:36

您不应该SyncLock实例类型。您总是希望对完全在类控制范围内的变量进行 SyncLock,但两者都不是。您应该声明一个私有New Object并将其用于您的SyncLock

Private lockObject as New Object()

...

SyncLock lockObject
   ...
End SyncLock

You shouldn't SyncLock the instance or the type. You always want to SyncLock on a variable that is fully within the control of the class, and neither of those are. You should declare a private New Object and use that for your SyncLock.

Private lockObject as New Object()

...

SyncLock lockObject
   ...
End SyncLock
秋意浓 2024-10-19 06:10:36

不幸的是,您需要在这里做一些不同的事情。

首先,我建议避免使用 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.

心作怪 2024-10-19 06:10:36

您应该在此处使用 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.

顾忌 2024-10-19 06:10:36

您可以用信号量代替线程计数器。如果您使用 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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文