0

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

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

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

所以我有几个问题:

主要问题:我是否应该_availableThreads在类中的任何地方都同步锁定 - 即在创建线程的方法中(创建线程时加 1)

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

  1. 我通常SyncLock是当前实例,但我已经看到了该类型的代码,那么同步锁定(当前实例)和SyncLock之间有什么区别?MeGetType(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
4

4 回答 4

6

你不应该SyncLock实例类型。你总是想要SyncLock一个完全在类控制范围内的变量,而这些都不是。您应该声明一个私有New Object并将其用于您的SyncLock.

Private lockObject as New Object()

...

SyncLock lockObject
   ...
End SyncLock
于 2011-01-14T20:11:41.780 回答
2

您应该在这里使用 Interlocked 类,即 Decrement() 方法来减少计数。是的,在任何地方都可以访问变量。

使用 SyncLock Me 与 SyncLock GetType(Me) 一样糟糕。您应该始终使用私有对象来锁定,这样没有人会意外导致死锁。黄金法则是不能锁定数据,只能阻止代码访问数据。由于代码是您的私有实现细节,因此持有锁定状态的对象也必须是私有细节。您的对象(我)和该对象的类型都不是私有的。允许其他代码意外锁定它。

于 2011-01-14T20:12:30.733 回答
2

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

首先,我建议避免使用 SyncLock,并使用 Interlocked.Increment 和 Interlocked.Decrement 来处理更改 _availableThreads。这将在没有锁的情况下为该变量提供线程安全。

话虽如此,您仍然需要在每次访问队列时使用 SyncLock - 如果它是从多个线程中使用的。如果您使用的是 .NET 4,另一种方法是改用新的ConcurrentQueue(Of T)类而不是 Queue。如果您使用 SyncLock,您应该创建一个只能由您的类访问的私有对象,并将其用于所有同步。

于 2011-01-14T20:28:07.647 回答
1

您可以用信号量代替线程计数器。如果使用 Semaphore,则无需退出 while 循环,也无需从 ThreadCompleted 事件处理程序调用 WorkThroughQueue()。Semaphore 是线程安全的,因此您可以在不锁定的情况下使用它。

http://www.albahari.com/threading/part2.aspx#_Semaphore

于 2011-01-14T20:37:06.147 回答