0

2 つのメソッドを持つクラスがあります。1 つは多数のスレッドを作成して実行するクラスを呼び出し、もう 1 つはそれらのスレッドが完了したときに発生するイベントを処理する (そして最初のメソッドを再度呼び出す) イベント ハンドラーです。

イベントを処理するメソッドは、イベントを発生させたスレッドで実行されることを理解しています。そのため、SyncLock実行中のスレッド数を示すメンバー変数を作成し、そこから 1 を引きます。

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

だから私はいくつかの質問があります:

主な質問:クラスのどこでもSyncLockする必要がありますか?_availableThreadsつまり、スレッドを作成するメソッド (スレッドが作成されると 1 が追加されます) で

この質問に関連する副次的な質問:

  1. 通常SyncLockは現在のインスタンスを使用しますが、代わりにタイプのコードを見たことがあるので、同期ロック(現在のインスタンス) とSyncLockの違いは何ですか?MeGetType(Me)

  2. 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クラスの完全な制御内にある変数を常に使用したいのですが、どちらもそうではありません。privateNew Objectを宣言し、それをSyncLock.

Private lockObject as New Object()

...

SyncLock lockObject
   ...
End SyncLock
于 2011-01-14T20:11:41.780 に答える
2

残念ながら、ここではいくつか異なることを行う必要があります。

まず、SyncLock を回避し、Interlocked.Increment と Interlocked.Decrement を使用して _availableThreads の変更を処理することをお勧めします。これにより、ロックなしでその変数にスレッド セーフが提供されます。

そうは言っても、複数のスレッドから使​​用されている場合は、キューへのすべてのアクセスの周りに SyncLock が必要です。別の方法として、.NET 4 を使用している場合は、Queue の代わりに新しいConcurrentQueue(Of T)クラスを使用するように変更することもできます。SyncLock を使用する場合は、クラスのみがアクセスできるプライベート オブジェクトを作成し、それをすべての同期に使用する必要があります。

于 2011-01-14T20:28:07.647 に答える
2

ここでは Interlocked クラス、Decrement() メソッドを使用してカウントを減らす必要があります。はい、変数がアクセスされる場所ならどこでも。

SyncLock Me の使用は、SyncLock GetType(Me) と同じくらい悪いです。だれもが誤ってデッドロックを引き起こすことがないように、常にプライベート オブジェクトを使用してロックする必要があります。黄金律は、データをロックすることはできないということです。コードがデータにアクセスするのをブロックすることしかできません。コードは非公開の実装の詳細であるため、ロック状態を保持するオブジェクトも非公開の詳細である必要があります。オブジェクト (Me) もそのオブジェクトの Type も非公開ではありません。他のコードが誤ってロックできるようにします。

于 2011-01-14T20:12:30.733 に答える
1

スレッド カウンターをセマフォに置き換えることができます。Semaphore を使用する場合、while ループを終了する必要はなく、ThreadCompleted イベント ハンドラから WorkThroughQueue() を呼び出す必要もありません。セマフォはスレッド セーフなので、ロックせずに使用できます。

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

于 2011-01-14T20:37:06.147 に答える