Die Community zu .NET und Classic VB.
Menü

Tipp-Upload: VB.NET 0278: Threading

 von 

Über den Tipp  

Dieser Tippvorschlag ist noch unbewertet.

Der Vorschlag ist in den folgenden Kategorien zu finden:

  • Sprachmerkmale

Dem Tippvorschlag wurden folgende Schlüsselwörter zugeordnet:
thread,asynchron,gui,einfrieren,invoke,begininvoke

Der Vorschlag wurde erstellt am: 12.06.2008 20:41.
Die letzte Aktualisierung erfolgte am 30.11.2008 15:37.

Zurück zur Übersicht

Beschreibung  

Zeitaufwändige Verarbeitungen führen zum "Einfrieren" der Anwendung. Denn während der Abarbeitung kann die Benutzeroberfläche (= GUI: Graphic User Interface) nicht auf Eingaben reagieren.
Abhilfe schafft man, indem man den "Job" zunächst isoliert, und dann in einen Nebenthread verlegt.
Anders als ein prozeduraler Aufruf kann ein parallel laufender Job kein Ergebnis zurückliefern (denn dann müsste gewartet werden, bis er fertig ist (=blockieren)).
Daher muß ein Job ggfs. sein Ergebnis durch Aufruf einer zusätzlichen Methode (oder durch Feuern eines Ereignisses) wieder in den Hauptthread einbringen.
Dieses Ergebnis-Einbringen muß außerdem zurückverlegt werden in den GUI-Thread, da Steuerelemente Ausnahmen werfen, wenn aus einem Nebenthread auf sie zugegriffen wird.

Die Klasse "CrossThread" bietet für die Verlegung eines Methoden-Aufrufes in einen Nebenthread die Methode "RunAsync" in 4 facher Überladung, um für bis zu 3 Argumente gewappnet zu sein. Desgleichen die Methode "RunGui" für die Rückverlagerung in den Gui-Thread.

Schwierigkeitsgrad

Schwierigkeitsgrad 2

Verwendete API-Aufrufe:

Download:

Download des Beispielprojektes [16,08 KB]

' Dieser Source stammt von http://www.activevb.de
' und kann frei verwendet werden. Für eventuelle Schäden
' wird nicht gehaftet.

' Um Fehler oder Fragen zu klären, nutzen Sie bitte unser Forum.
' Ansonsten viel Spaß und Erfolg mit diesem Source!
'
' Beachten Sie, das vom Designer generierter Code hier ausgeblendet wird.
' In den Zip-Dateien ist er jedoch zu finden.

' --------- Anfang Projektgruppe SimpleThreading.sln ---------
' -------- Anfang Projektdatei SimpleThreading.vbproj --------
' --------------- Anfang Datei CrossThread.vb  ---------------
' IDE-Voreinstellungen:
' Option Explicit On
' Option Strict On

' "My Project"-Einstellungen:
' Imports Microsoft.VisualBasic.ControlChars
' Imports System.Windows.Forms
' Imports System

#Region " VB2008 hat diese 3 generischen Delegaten bereits deklariert, daher sollten sie " & _
    "bei Verwendung von VB2008 nicht noch einmal deklariert werden."

Public Delegate Sub Action()
Public Delegate Sub Action(Of T1, T2)(ByVal Arg1 As T1, ByVal Arg2 As T2)
Public Delegate Sub Action(Of T1, T2, T3)(ByVal Arg1 As T1, ByVal Arg2 As T2, ByVal Arg3 As T3)

#End Region

''' <summary>
''' Stellt Methoden bereit, mit denen ein beliebiger Methoden-Aufruf mit bis zu 3 Argumenten
''' in einen Nebenthread verlegt werden kann, bzw. aus einem Nebenthread in den Hauptthread
''' </summary>
Public Class CrossThread

    Public Shared Sub RunAsync(Of T1, T2, T3)(ByVal Action As Action(Of T1, T2, T3), ByVal _
        Arg1 As T1, ByVal Arg2 As T2, ByVal Arg3 As T3)

        ' Aufruf von Action.EndInvoke() gewährleisten, indem er als Callback-Argument
        ' mitgegeben wird
        Action.BeginInvoke(Arg1, Arg2, Arg3, AddressOf Action.EndInvoke, Nothing)

    End Sub

    Public Shared Sub RunAsync(Of T1, T2)(ByVal Action As Action(Of T1, T2), ByVal Arg1 As _
        T1, ByVal Arg2 As T2)

        Action.BeginInvoke(Arg1, Arg2, AddressOf Action.EndInvoke, Nothing)

    End Sub

    Public Shared Sub RunAsync(Of T1)(ByVal Action As Action(Of T1), ByVal Arg1 As T1)

        Action.BeginInvoke(Arg1, AddressOf Action.EndInvoke, Nothing)

    End Sub

    Public Shared Sub RunAsync(ByVal Action As Action)

        Action.BeginInvoke(AddressOf Action.EndInvoke, Nothing)

    End Sub

    Private Shared Function GuiCrossInvoke(ByVal Action As [Delegate], ByVal ParamArray Args( _
        ) As Object) As Boolean

        If Application.OpenForms.Count = 0 Then

            ' wenn kein Form mehr da ist, so tun, als ob das Invoking ausgeführt wäre
            Return True
        End If

        If Application.OpenForms(0).InvokeRequired Then
            Application.OpenForms(0).BeginInvoke(Action, Args)
            Return True
        End If

    End Function

    Public Shared Sub RunGui(Of T1, T2, T3)(ByVal Action As Action(Of T1, T2, T3), ByVal Arg1 _
        As T1, ByVal Arg2 As T2, ByVal Arg3 As T3)

        ' falls Invoking nicht erforderlich, die Action direkt ausführen
        If Not GuiCrossInvoke(Action, Arg1, Arg2, Arg3) Then Action(Arg1, Arg2, Arg3)

    End Sub

    Public Shared Sub RunGui(Of T1, T2)(ByVal Action As Action(Of T1, T2), ByVal Arg1 As T1, _
        ByVal Arg2 As T2)

        If Not GuiCrossInvoke(Action, Arg1, Arg2) Then Action(Arg1, Arg2)

    End Sub

    Public Shared Sub RunGui(Of T1)(ByVal Action As Action(Of T1), ByVal Arg1 As T1)

        If Not GuiCrossInvoke(Action, Arg1) Then Action(Arg1)

    End Sub

    Public Shared Sub RunGui(ByVal Action As Action)

        If Not GuiCrossInvoke(Action) Then Action()

    End Sub

End Class

' ---------------- Ende Datei CrossThread.vb  ----------------
' ------------ Anfang Datei frmSimpleThreading.vb ------------
Imports System.Threading

Public Class frmSimpleThreading

    Private Function GetFibonacci(ByVal n As Long) As Long

        ' die Fibonacci-Zahl wird für irgendwas in der Statistik gebraucht, k.A.
        ' CPU-Stresser, da pro Rekursion 2 rekursive Aufrufe. Bei z.B. n=20 ergeben sich ca.
        ' 2^18 Aufrufe
        If n < 3 Then Return 1
        Return GetFibonacci(n - 1) + GetFibonacci(n - 2)

    End Function

    Private Sub Run1Second(ByVal FiboInput As Integer, ByVal IsAsync As Boolean)

        Dim Timeout As Date = Now + TimeSpan.FromSeconds(1)
        Dim Result As Long = GetFibonacci(FiboInput)
        Dim Counter As Long = 1L

        Do

            ' wiederholt GetFibonacci() so lange, bis 1 Sekunde herum ist.
            GetFibonacci(FiboInput)
            Counter += 1
        Loop Until Now > Timeout

        If IsAsync Then
            CrossThread.RunGui(AddressOf DisplayResult, FiboInput, Result, Counter)

        Else

            DisplayResult(FiboInput, Result, Counter)
        End If

    End Sub

    Private Sub DisplayResult(ByVal FiboInput As Integer, ByVal Result As Long, ByVal Counter _
        As Long)

        Me.lbFiboResult.Text = String.Concat("Fibonacci (", FiboInput, ") = ", Result, Lf, _
            Counter, " times calculated")

    End Sub

    Private Sub Button_Click(ByVal sender As Object, ByVal e As EventArgs) _
              Handles btBlockGUI.Click, btAsync.Click

        Me.lbFiboResult.Text = ""
        lbFiboResult.Refresh()

        Dim FiboInput As Integer = CInt(updFiboInput.Value)

        Select Case True

            Case sender Is btBlockGUI

                ' normaler Aufruf (Gui-Blockade)
                Run1Second(FiboInput, False)

            Case sender Is btAsync

                ' derselbe Aufruf, aber asynchron
                CrossThread.RunAsync(AddressOf Run1Second, FiboInput, True)

        End Select

    End Sub

End Class

' ------------- Ende Datei frmSimpleThreading.vb -------------
' --------- Ende Projektdatei SimpleThreading.vbproj ---------
' ---------- Ende Projektgruppe SimpleThreading.sln ----------

	

Diskussion  

Diese Funktion ermöglicht es, Fragen, die die Veröffentlichung des Tipps betreffen, zu klären, oder Anregungen und Verbesserungsvorschläge einzubringen. Nach der Veröffentlichung des Tipps werden diese Beiträge nicht weiter verlinkt. Allgemeine Fragen zum Inhalt sollten daher hier nicht geklärt werden.

Um eine Diskussion eröffnen zu können, müssen sie angemeldet sein.