2011-01-14 6 views
0

Ich habe eine Klasse, die zwei Methoden enthält, ruft eine Klasse, die eine Anzahl von Threads erstellt und ausführt, die andere ist ein Ereignishandler, der ein Ereignis behandelt, wenn diese Threads ausgelöst werden vollständig (und ruft dann die erste Methode erneut auf).Wo und was in diesem Code zu synchronisieren Sperre

Ich verstehe, dass die Methode, die das Ereignis behandelt, in dem Thread ausgeführt wird, der das Ereignis ausgelöst hat. So als solche ich SyncLock eine Membervariable, die besagt, wie viele Threads laufen und subtrahieren davon:

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

Also habe ich ein paar Fragen:

Haupt Frage: Soll ich SyncLock‘werden _availableThreads überall in der Klasse ing - also in der Methode, die die Fäden (die 1 hinzufügt, wenn ein Thread erstellt wird) erzeugt

Side Fragen zu dieser Frage bezogen werden:

  1. Ich würde normalerweise die aktuelle Instanz synchronisieren, aber ich habe Code gesehen, der stattdessen den Typ SyncLocks, also was ist der Unterschied zwischen Synchronisierungsverriegelung Me (Aktuelle Instanz) und GetType(me)?

  2. Würde es einen Leistungsunterschied zwischen den beiden geben? und gibt es etwas kleineres, das ich für das oben Gesagte sperren könnte, was nichts anderes betrifft - vielleicht ein separates "Vorhängeschloss" -Objekt, das nur zum Zwecke der Blockierung von Dingen in einer Klasse erstellt wurde?

Hinweis: Der einzige Zweck der _available Threads zu steuern, wie viele Threads zu einem bestimmten Zeitpunkt ausgeführt werden kann und die Threads verarbeiten Aufträge, die Stunden dauern kann, laufen.

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 

Antwort

2

Leider müssen Sie hier ein paar Dinge anders machen.

Zunächst einmal würde ich empfehlen, SyncLock zu vermeiden und Interlocked.Increment und Interlocked.Decrement zu verwenden, um _availableThreads zu ändern.Dies bietet Thread-Sicherheit für diese Variable ohne eine Sperre.

Das heißt, Sie benötigen immer noch eine SyncLock um jeden Zugriff auf Ihre Warteschlange - wenn es aus mehreren Threads verwendet wird. Eine Alternative, wenn Sie .NET 4 verwenden, wäre die Verwendung der neuen Klasse ConcurrentQueue(Of T) anstatt der Warteschlange. Wenn Sie SyncLock verwenden, sollten Sie ein privates Objekt erstellen, auf das nur Ihre Klasse zugreifen kann, und es für die gesamte Synchronisation verwenden.

+0

Sehr interessant ... Ich war von der Sperrungs Klasse nicht bekannt. Vielen Dank. –

+0

Guter Punkt über die Warteschlange - ich benutze 3,5, also ist das leider nicht möglich. Ist es möglich, dem Thread wieder beizutreten und die erste Methode aufzurufen? Würdest du wissen, wie das geht? - möglicherweise mit thread.join? –

+1

@Mr Shoubs: Nein, nicht wirklich. Wenn Sie 3.5 verwenden, empfehle ich die Verwendung eines SyncLock für ein privates Objekt für den Zugriff auf die Warteschlange - sowohl für die Warteschlange als auch für die Warteschlange. –

6

Sie sollten nicht SyncLock die Instanz oder der Typ. Sie möchten immer SyncLock für eine Variable, die vollständig innerhalb der Kontrolle der Klasse ist, und keiner von denen sind. Sie sollten eine private New Object deklarieren und diese für Ihre SyncLock verwenden.

Private lockObject as New Object() 

...

SyncLock lockObject 
    ... 
End SyncLock 
2

Sie sollten hier die Interlocked-Klasse verwenden, die Decrement() Methode der Zählung zu verringern. Ja, überall wird auf die Variable zugegriffen.

Verwenden von SyncLock Me ist so schlecht wie SyncLock GetType (Me). Sie sollten immer ein privates Objekt zum Sperren verwenden, so dass niemand versehentlich einen Deadlock verursachen kann. Die goldene Regel ist, dass Sie Daten nicht sperren können, Sie können Code nur blockieren, um auf Daten zuzugreifen. Da der Code Ihr privates Implementierungsdetail ist, muss das Objekt, das den Sperrstatus enthält, auch ein privates Detail sein. Weder dein Objekt (Ich) noch der Typ dieses Objekts ist privat. Anderen Code zulassen, um es versehentlich zu sperren.

1

Sie können den Thread-Zähler durch Semaphore ersetzen. Wenn Sie Semaphore verwenden, müssen Sie die while-Schleife nicht beenden, und es ist auch nicht erforderlich, WorkThroughQueue() vom ThreadCompleted-Ereignishandler aufzurufen. Semaphore ist threadsicher, sodass Sie es ohne Sperren verwenden können.

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

+0

Das sind gute Informationen, danke! Ich werde sowohl die Methode, die ich benutze, als auch den Semaphor untersuchen, da ich lernen möchte, dem Thread beizutreten. –