mehrere Threads starten, überwachen und auf Beendigung warten

Mi, 02.06.2010 - 17:29 -- admin

Für diverse kleine Projekte und Anwendungen in VB.NET verarbeite ich Listen von Aufgaben und Jobs. Meistens lässt man diese logischerweise hintereinander ablaufen. Bei größeren Listen und und intensiveren Rechenoperationen wünscht man sich allerdings eine parallele Abarbeitung in Threads, so dass die Aktionen schneller fertig sind.
Da es mir mit der Zeit zu aufwendig geworden ist die Threadprogammierung in jede Anwendung neu zu entwickeln, habe ich eine Klasse entwickelt, die man in seine eigene Klasse einbinden kann. Voraussetzung ist dass die abzuarbeiten Aktionen ein einer Arraylist vorliegen und die einzelnen Elemente über einen Funktionsaufruf abgearbeitet werden.

Beispiel

Beispiel: Kommt noch...

Funtionen und Properties

  • CurrentPostion: Das aktuell bearbeitende Element
  • ItemsCount: zeigt die Gesamtanzahl, der zu verarbeiteten Elemente an
  • WaitForExit: Loop, die solange läuft bis alle Elemente abgearbeitet wurden
  • HasExited: gibt true zurück, wenn alle Elemente abgearbeitet wurden
  • Start: startet alle Threads, wartet nicht auf die Beendigung der Threads. Um zu Überprüfen, ob alle Elemente durchlaufen wurden WaitForExit oder HasExited nutzen
VCS File: 

/trunk/ThreadWorker/ThreadWorker.vb

'$Id: ThreadWorker.vb 18 2010-03-24 13:29:02Z espendiller $
'$HeadURL: file:///var/svn-repos/vbnettools/trunk/ThreadWorker/ThreadWorker.vb $
''' <summary>
''' Simple template for Threads usage
''' </summary>
''' <remarks></remarks>
Public MustInherit Class ThreadWorker
    Private _CurrentPosition As Double = 0
    Private _MaxThreads As Integer = 4
 
    Private _ThreadsArray As New List(Of Threading.Thread)
    Private _Items As New ArrayList
    Private _ItemsCount As Integer
 
    Public Event Finished()
    Public Event PercentDone(ByVal Percent As Double)
    Public Event ItemFinished(ByVal CurrentPosition As Integer, ByVal Thread As Integer, ByVal ReturnString As String)
#Region "Properties"
    ''' <summary>
    ''' How many Threads do we want? Default = 4
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Property MaxThreads() As Integer
        Get
            Return _MaxThreads
        End Get
        Set(ByVal value As Integer)
            _MaxThreads = value
        End Set
    End Property
    ''' <summary>
    ''' A list with Object to work on
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Property Items() As ArrayList
        Get
            Return _Items
        End Get
        Set(ByVal value As ArrayList)
            _Items = value
            _ItemsCount = _Items.Count
        End Set
    End Property
    ''' <summary>
    ''' Get the last finished item as integer
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    ReadOnly Property CurrentPostion() As Integer
        Get
            Return _CurrentPosition
        End Get
    End Property
    ''' <summary>
    ''' Get overall items count 
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    ReadOnly Property ItemsCount() As Integer
        Get
            Return _ItemsCount
        End Get
    End Property
#End Region
    Sub Start()
        Call StartBefore()
 
        'add wanted threads to array for later usage
        For i As Integer = 0 To _MaxThreads - 1
            _ThreadsArray.Add(New Threading.Thread(AddressOf WorkerThread))
        Next
 
        'walk through array of threads and start each one
        For i As Integer = 0 To _ThreadsArray.Count - 1
            Dim myThread As Threading.Thread = _ThreadsArray.Item(i)
            myThread.Start(i)
            System.Threading.Thread.Sleep(10)
        Next
 
        'a simple thread the will raise the Finished() event if all done
        Dim FishinedThread As New Threading.Thread(AddressOf WaitForExitThreadEvent)
        FishinedThread.Start()
 
    End Sub
    ''' <summary>
    ''' return true if all threads are finished
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function HasExited() As Boolean
        Dim back As Boolean = True
        For Each myThread As Threading.Thread In _ThreadsArray
            If myThread.IsAlive = True Then
                Return False
            End If
        Next
        Return back
    End Function
    ''' <summary>
    ''' wait for all threads to be finished in parent thread
    ''' </summary>
    ''' <remarks></remarks>
    Sub WaitForExit()
        Do While Me.HasExited = False
            System.Threading.Thread.Sleep(500)
        Loop
    End Sub
    ''' <summary>
    ''' Helper for WaitForExit()
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub WaitForExitThreadEvent()
        Call WaitForExit()
        RaiseEvent Finished()
    End Sub
    ''' <summary>
    ''' Sub started by threads and walk through array list
    ''' </summary>
    ''' <param name="Thread"></param>
    ''' <remarks></remarks>
    Private Sub WorkerThread(ByVal Thread As Integer)
        Do While _CurrentPosition < _ItemsCount
            'how much is done all ready in percent
            RaiseEvent PercentDone(Math.Round(_CurrentPosition / _ItemsCount * 100, 1))
 
            'for thread safety: before do something with this variable set new value 
            _CurrentPosition += 1
 
            'do the funny stuff
            Dim BackStr As String = WorkItem(_CurrentPosition - 1, Thread)
 
            'Calls WorkItem return text and raise the event
            RaiseEvent ItemFinished(_CurrentPosition - 1, Thread, BackStr)
        Loop
    End Sub
    ''' <summary>
    ''' This function does working stuff; you must override it
    ''' </summary>
    ''' <param name="CurrentPosition">The Current Item as Integer</param>
    ''' <param name="Thread">The Thread which did the work</param>
    ''' <returns>a return string value that can use in ItemFinished</returns>
    ''' <remarks></remarks>
    MustOverride Function WorkItem(ByVal CurrentPosition As Integer, ByVal Thread As Integer) As String
 
    ''' <summary>
    ''' Optional: Function that is perfomed before we Start() our threads
    ''' </summary>
    ''' <remarks></remarks>
    Overridable Sub StartBefore()
        'nothing to do
    End Sub
End Class