在此代码中锁定的位置和内容

时间:2011-01-14 20:06:34

标签: vb.net multithreading thread-safety synclock

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

我知道处理事件的方法在引发事件的线程中运行。因此,我SyncLock一个成员变量,说明正在运行多少个线程并从中减去一个:

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

所以我有几个问题:

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

与此问题相关的问题:

  1. 我通常会同步SyncLock当前实例,但我看到SyncLocks类型的代码,所以同步锁定Me(当前实例)和GetType(me)之间的区别是什么

  2. 两者之间会有性能差异吗?并且有什么小的我可以锁定以上不会影响其他任何东西 - 可能是一个单独的'挂锁'对象创建的唯一目的是锁定类中的东西?

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

4 个答案:

答案 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()。信号量是线程安全的,因此您可以在不锁定的情况下使用它。

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