Eventhandler und Threads in Einklang bringen

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Eventhandler und Threads in Einklang bringen

    Mahlzeit ihr lieben!

    Problem: Ich sende per RaiseEvent ein Event aus meiner Klasse an meine Form und erhalte folgenden Fehler:
    Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement textbox1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.


    Ursache: Innerhalb meiner Klasse arbeite ich mit einem websocket und entsprechenden Handlern:

    VB.NET-Quellcode

    1. Public Sub New()
    2. websocket = New WebSocket4Net.WebSocket(Me.apiUrl,,,, Me.apiFriendlyName, Me.apiOrigin, WebSocket4Net.WebSocketVersion.Rfc6455,, Security.Authentication.SslProtocols.Tls12)
    3. AddHandler websocket.Opened, AddressOf socketOpened
    4. AddHandler websocket.Error, AddressOf socketError
    5. AddHandler websocket.Closed, AddressOf socketClosed
    6. AddHandler websocket.MessageReceived, AddressOf socketMessage
    7. AddHandler websocket.DataReceived, AddressOf socketDataReceived
    8. End Sub


    Hier wird das Event ausgelößt..

    VB.NET-Quellcode

    1. Private Sub socketOpened(s As Object, e As EventArgs)
    2. Me._isConnected = True
    3. RaiseEvent Connected() ' <--- Hier wird das Event ausgelößt..
    4. End Sub


    Und hier kommt es zur Ausnahme:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Public WithEvents API As New Webmana.BitcoinAPI(My.Settings.api_key, My.Settings.api_secret, False, False)
    3. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    4. API.connect()
    5. End Sub
    6. Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
    7. API.disconnect()
    8. End Sub
    9. Private Sub API_Connected() Handles API.Connected
    10. TextBox1.Text = "Verbunden."
    11. End Sub
    12. End Class


    Mein Lösungsansatz:

    Da ich hier tatsächlich Threatübergreifend arbeite, dachte ich mir, dass ich einen Forms.Timer in meine Klasse einbaue und die Events nicht direkt aus den Websocket-Handlern aufrufe, sondern in einer List zwischenspeichere und dann über das Tick-Ereignis des Timers auslöse.
    Wenn ich aber eine List(of String) für den Event-Namen und eine List(of Args) für die Event-Argumente erstelle, wie löse ich dann im Tick-Ereignis des Timers das Event aus?

    VB.NET-Quellcode

    1. Namespace Webmana
    2. Public Class BitcoinAPI
    3. Public Event Connected(sender As object)
    4. Private eventNames As New List(Of String)
    5. Private eventArgs As New List(Of Object)
    6. Private Sub listEvent(evName As String, evArg As Object)
    7. eventNames.add(evName)
    8. eventArgs.add(evArg)
    9. End Sub
    10. Private WithEvents eventRaiser As New Timer
    11. Public Sub New()
    12. eventRaiser.Interval = 1
    13. eventRaiser.Start()
    14. End Sub
    15. Private Sub eventRaiser_Tick(sender As Object, e As EventArgs) Handles eventRaiser.Tick
    16. For i As Integer = 0 To eventNames.Count() - 1
    17. RaiseEvent eventNames(i)(eventArgs(i)) '<-- Gibt es überhaupt so eine Möglichkeit? Ein Event anhand seines Namens aus einem String aufzurufen?
    18. Next
    19. End Sub
    20. End Class
    21. End Namespace


    Vielleicht wisst ihr ja eine Lösung oder einen anderen Weg, wie ich möglichst Realtime meine Events an die Form gesendet bekomme und dort auf Steuerelemente zugreifen kann, obwohl ich mich in einem anderen Thread befinde.

    Danke schonmal und Lg Hassowuff :thumbsup:
    __________________________
    01:
    CLS : SCREEN 12
    02: LINE (0, 20)-(640, 22), 15, BF
    03: ECHO "MFG HASSOWUFF"
    [F5]
    Nix mit Timer. Belass es beim Event-Raising und schreib im EventHandler:

    VB.NET-Quellcode

    1. Me.Invoke(Sub()
    2. 'hier alles mit CE-(= Steuerelemente)manipulation
    3. End Sub)
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Vielen Dank, dass funktioniert und macht mich überglücklich, denn die Lösung mit dem Timer hätte mir selbst gehörig gestunken!
    __________________________
    01:
    CLS : SCREEN 12
    02: LINE (0, 20)-(640, 22), 15, BF
    03: ECHO "MFG HASSOWUFF"
    [F5]

    ErfinderDesRades schrieb:

    Man merkt den Unterschied nicht
    Wenn da was synchronisiert werden soll, können .Invoke() und .BeginInvoke() zu unterschiedlichen Ergebnissen führen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Mag sein. Ich weiss grad nicht, was du mit "wenn was synchronisiert werden soll" meinst, und unter welchen Umständen (etwa hier) es relevant ist.
    In meiner Praxis war bislang noch nie Control.Invoke wirklich erforderlich - also habich wohl noch nie was synchronisieren müssen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    @ErfinderDesRades GUI-Anzeige eines Protokolls von Hardware-Zugriffen aus verschiedenen Threads z.B.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @ErfinderDesRades Die Reihenfolge, in der die Log-Einträge angezeigt werden, ich hab da mal n Test zu gemacht.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich kann mich jetzt nicht mehr an den Einzelfall erinnern, aber ich hatte auch schon das Problem, dass BeginInvoke auf einen Fehler lief, während Invoke problemlos funktionierte.
    Ich habe es damals nicht genau analysiert, aber es gibt wohl unterschiedliches Verhalten.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    Da gab's auf stackoverflow ein Beispiel. Achtung, Spoileralarm für alle, die noch vorhaben "Die üblichen Verdächtigen" zu schauen.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    RodFromGermany schrieb:

    @ErfinderDesRades Die Reihenfolge, in der die Log-Einträge angezeigt werden, ich hab da mal n Test zu gemacht.
    Ja gut, wenn das nicht zeitkritisch ist, dass die NebenThreads sich gegenseitig ausbremsen, weil sie beim Gui Schlange stehen müssen, bis dieses die Darstellung des LogEntries unter DachnFach hat ...
    Ansonsten würde ich dazu neigen, dass sie ihre Logmeldungen in eine ConcurrentQueue einspeisen, aus welcher sich wiederum das Gui bedient.
    Wenn dann immer noch die Reihenfolge nicht stimmt, evtl. im GuiThread iwie nach Timestamp sortieren.