Die Community zu .NET und Classic VB.
Menü

Tipp-Upload: VB.NET 0017: Threadübergreifende Events

 von 

Über den Tipp  

Dieser Tippvorschlag ist noch unbewertet.

Der Vorschlag ist in den folgenden Kategorien zu finden:

  • Fenster
  • Sonstiges
  • Sprachmerkmale
  • Timer und Zeiten

Dem Tippvorschlag wurden folgende Schlüsselwörter zugeordnet:
Threading, generisch, Ereignis, Event

Damit er übernommen werden kann, müssen noch Änderungen daran vorgenommen werden. Sofern Sie der Autor sind, können Sie sich anmelden, um die Liste einzusehen.

Der Vorschlag wurde erstellt am: 29.07.2007 19:30.
Die letzte Aktualisierung erfolgte am 04.02.2009 21:59.

Zurück zur Übersicht

Beschreibung  

Will man von einem NebenThread aus benutzerdefinierte Ereignisse auslösen, deren Verarbeitung schließlich angezeigt wird, so empfiehlt es sich, schon das Auslösen selbst mittels Control.Invoke() in den Hauptthread zu "überführen", ansonsten eine Fehlermeldung wegen ungültigem threadübergreifendem Aufruf erfolgt.
Es handelt sich um einen Anwendungsfall der in  Tippvorschlag 278 (Threading) vorgestellten Vorgehensweise. Vorraussetzung ist natürlich, daß das Ereignis konform zu den Framework-Konventionen implementiert ist, siehe auch  Tippvorschlag 283 (Ereignisse implementieren)

Schwierigkeitsgrad

Schwierigkeitsgrad 2

Verwendete API-Aufrufe:

Download:

Download des Beispielprojektes [13,92 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 CountDown.sln ------------
' ----------- Anfang Projektdatei CountDown.vbproj -----------
' ---------------- Anfang Datei CountDown.vb  ----------------
' IDE-Voreinstellungen:
' Option Explicit On
' Option Strict On

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

Imports System.Threading

Public Class CountDown

#Region "benutzerdefinierte Events"

    Public Class TickEventArgs

        Inherits EventArgs

        Public ReadOnly Counter As Integer

        Public Sub New(ByVal Counter As Integer)

            Me.Counter = Counter

        End Sub

    End Class ' TickEventArgs

    ' Tick verwendet einen generischen Eventhandler für TickEventArgs
    Public Event Tick As EventHandler(Of TickEventArgs)

    Protected Overridable Sub OnTick(ByVal e As TickEventArgs)

        RaiseEvent Tick(Me, e)

    End Sub

    ' Finished verwendet den Standard-Eventhandler für EventArgs.Empty
    Public Event Finished As EventHandler

    Protected Overridable Sub OnFinished()

        RaiseEvent Finished(Me, EventArgs.Empty)

    End Sub

#End Region ' benutzerdefinierte Events

    ' System.Threading.Timer läuft im NebenThread
    Private _AsyncTimer As New System.Threading.Timer(AddressOf AsyncTimer_Callback, Nothing, _
        Timeout.Infinite, Timeout.Infinite)

    Private _Counter As Integer

    Public Sub Start(ByVal InitValue As Integer)

        _Counter = InitValue
        _AsyncTimer.Change(dueTime:=0, period:=1000)  ' dueTime=0: sofort ticken

    End Sub

    Private Sub AsyncTimer_Callback(ByVal state As Object)

        ' Der Fehler ist reproduzierbar, indem die folgende Zeile statt der übernächsten ausgeführt
        ' wird. Auch gut erkennbar, wie ein beliebiger synchroner Aufruf umzuschreiben ist
        ' OnTick(New TickEventArgs(_Counter))
        CrossThread.RunGui(AddressOf OnTick, New TickEventArgs(_Counter))  ' Tick raisen

        If _Counter = 0 Then
            _AsyncTimer.Change(Timeout.Infinite, Timeout.Infinite)    ' stop timer
            CrossThread.RunGui(AddressOf OnFinished)              ' Finished raisen
        End If

        _Counter -= 1                                       ' "down-counten" ;)

    End Sub

End Class

' ----------------- Ende Datei CountDown.vb  -----------------
' --------------- Anfang Datei CrossThread.vb  ---------------
''' <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 frmCountDown.vb ---------------
Public Class frmCountDown

    Private WithEvents _CountDown As New CountDown

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
              Handles Button1.Click

        _CountDown.Start(10)

    End Sub

    Private Sub _CountDown_Tick( _
              ByVal sender As Object, _
              ByVal e As CountDown.TickEventArgs) Handles _CountDown.Tick

        Me.Label1.Text = e.Counter.ToString

    End Sub

    Private Sub _CountDown_Finished(ByVal sender As Object, ByVal e As System.EventArgs) _
              Handles _CountDown.Finished

        MsgBox("Go!")

    End Sub

End Class

' ---------------- Ende Datei frmCountDown.vb ----------------
' ------------- Anfang Datei UsefulDelegates.vb  -------------
' Compiler-Direktive: Code nur aktivieren, wenn die VB-Compiler-Version < 9.0 ist
#If VBC_VER < 9.0 Then

' diese 7 generischen Delegaten hat Framework 3 dem Framework 2 vorraus.
' Bis diese Datei eingebunden wird ;)
' Sie decken **jedes** Delegaten-Einsatzgebiet ab (Ausnahme: Delegaten mit ByRef-Parametern).

' ( Im FW 2 bereits bekannt: Public Delegate Sub Action(Of T)(ByVal arg As T) )
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)

Public Delegate Function Func(Of T)() As T
Public Delegate Function Func(Of T1, T2)(ByVal arg1 As T1) As T2
Public Delegate Function Func(Of T1, T2, T3)(ByVal arg1 As T1, ByVal arg2 As T2) As T3

Public Delegate Function Func(Of T1, T2, T3, T4)(ByVal arg1 As T1, ByVal arg2 As T2, ByVal _
    arg3 As T3) As T4

#End If

' -------------- Ende Datei UsefulDelegates.vb  --------------
' ------------ Ende Projektdatei CountDown.vbproj ------------
' ------------- Ende Projektgruppe CountDown.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.