Event aus anderem Thread?



  • Hallo,

    ich habe folgendes Problem in meiner Windows-Forms Anwendung: Und zwar habe ich eine Form mit einem SerialPort. Zu diesem füge ich einen Event hinzu wenn ich Daten empfange, etwa so:

    serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);
    

    Per Button-Click kann der Nutzer jetzt den Port auslesen. Während dieser Zeit deaktiviere ich die Form mittels this.Enabled = false; damit der Benutzer nicht mehr mit der Form interagieren kann solange die Daten ausgetauscht werden.

    Problem ist jetzt, dass ich ja in meinem Callback serialPort1_DataReceived Bescheid kriege wenn alles fertig gelesen ist.

    Deswegen will ich in dieser callback Funktion per this.Enabled = true; die Form wieder aktivieren. Leider bekomme ich wenn ich das versuche eine Exception mit der Meldung "Ungültiger Threadübergreifender Zugriff", da sich der callback anscheinend in einem anderen Thread befindet.

    Jetzt meine Frage, wie löse ich dieses Problem sauber? Kann ich von der callback Funktion aus einen weiteren Event in meinem ursprünglichen Thread triggern oder wie macht man das?



  • happystudent schrieb:

    Jetzt meine Frage, wie löse ich dieses Problem sauber?

    Genau so:

    happystudent schrieb:

    Kann ich von der callback Funktion aus einen weiteren Event in meinem ursprünglichen Thread triggern oder wie macht man das?

    Ja.
    Mit BeginInvoke

    ps: Es gibt auch die synchrone Variante "Invoke". Verwende ich aber kaum, da potentiell problematisch. Kann zu Deadlocks führen wenn man nicht aufpasst, und vor allem will man den originalen Callback-Thread auch meist nicht all zu lange blockieren.



  • hustbaer schrieb:

    Ja.
    Mit BeginInvoke

    Ahhh, "Invoke" war das Stichwort das ich gebraucht hab.

    Damit hab ichs jetzt hingekriegt, vielen Dank 👍



  • Hallo,

    nochmal eine Frage zu dem Thema, und zwar wie sollte man vorgehen wenn man dieses Verfahren jetzt auch für Klassen will die nicht von Windows.Form erben (also bei eigenen Klassen, die auch einen SerialPort beinhalten)?

    Weil solche Klassen haben ja keine Invoke / BeginInvoke Methoden bzw. InvokeRequired ...



  • z.B. SynchronizationContext.Current.Send/Post

    Aber warum benötigst du dann ein "Invoke", wenn du kein GUI-Element hast, welches du ansprechen möchtest?

    Lies dir auch mal [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke) durch.



  • Th69 schrieb:

    z.B. SynchronizationContext.Current.Send/Post

    Aber warum benötigst du dann ein "Invoke", wenn du kein GUI-Element hast, welches du ansprechen möchtest?

    Lies dir auch mal [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke) durch.

    Also der Hintergrund ist, dass ich Daten aus einem SerialPort gerne "auf einmal" auslesen wollen würde, und zwar wie als würde ich eine Funktion aufrufen. Also etwa so:

    var sm = new SerialManager(); // Der hier (eigene Klasse) soll sich intern um alles kümmern
    List<string> responseList = sm.readAllFromPort(); // Lese alles in eine Liste und gib diese zurück
    

    Soweit so gut, mit Hilfe von WaitOne ist das auch recht einfach, man liest einfach in readAllFromPort so lange bis man alle Daten hat, triggert dann ein "fertig" und gibt die Liste mit allen Daten zurück.

    Problem ist halt dass das WaitOne den thread blockiert und deswegen die GUI während gelesen wird nicht mehr ansprechbar ist.

    Ich hätte halt gerne beides: Die Einfachheit beim Aufruf (letztendlich ein 1-Zeiler) um alles einzulesen aber gleichzeitig dass die GUI nicht einfriert - also nur die Funktion in der readAllFromPort "stehen bleibt", nicht jedoch die ganze (Windows Form) Klasse.



  • happystudent schrieb:

    Ich hätte halt gerne beides: Die Einfachheit beim Aufruf (letztendlich ein 1-Zeiler) um alles einzulesen aber gleichzeitig dass die GUI nicht einfriert - also nur die Funktion in der readAllFromPort "stehen bleibt", nicht jedoch die ganze (Windows Form) Klasse.

    Das beisst sich leider.
    Es gibt so coole Sachen wie async/await (-> Google), aber um das richtig anzuwenden muss man auch wissen was das alles bewirkt. Und ne ungefähre Vorstellung davon haben was hinter der Bühne abgeht.

    Weil einem das toll "asynchronisierte" Programm sonst schnell um die Ohren fliegt, bzw. anfängt ganz komische Dinge zu tun.
    Speziell wenn ungeduldige User ungeduldig drin rumklicken. Und so bestimmte Dinge zu bestimmten Zeiten machen, wo man beim Testen im Leben nicht draufgekommen wäre.

    Beispielsweise hast du bei async/await (bzw. generell jeder Technik die du verwenden kannst damit dir "die GUI nicht einfriertt") das Problem, dass Window Messages bearbeitet werden während deine "asynchrone" Funktion wartet.
    D.h. an der Stelle

    var sm = new SerialManager();
    List<string> responseList = sm.readAllFromPort(); // <------- HIER
    DoStuffWithResult(responseList);
    

    können beliebige Event-Handler (Button-Clicked, ...), Timer etc. aufgerufen werden. Das schliesst auch Dinge ein wie z.B. das Schliessen des Fensters.

    Und wenn die asynchrone Funktion (hier readAllFromPort) dann fertig ist, dann läuft die Funktion auf einmal an der Stelle "DoStuffWithResult()" weiter. Und dazwischen hat sich u.U. alles verändert. D.h. das Fenster könnte bereits geschlossen sein oder was auch immer.

    Natürlich kann man das alles lösen*, aber wie gesagt: man muss wissen was da abgeht, und vor allem ist es auch wieder Arbeit.
    Also ganz so easy wird es nicht, egal wie du es anpackst.

    *: z.B. indem man sämtliche Controls disabled, das Schliessen des Fensters verhindert etc. bevor man die asynchrone Funktion aufruft, und nachdem sie fertig gelaufen ist alles wieder rückgängig macht.



  • hustbaer schrieb:

    Es gibt so coole Sachen wie async/await (-> Google), aber um das richtig anzuwenden muss man auch wissen was das alles bewirkt. Und ne ungefähre Vorstellung davon haben was hinter der Bühne abgeht.

    Ok, das sieht ziemlich genau nach dem aus was ich brauche. Werde mich mal versuchen einzuarbeiten, liest sich auf jeden Fall sehr vielversprechend, Danke 👍

    Bezüglich ungeduldiger User denke ich nicht dass es Probleme geben wird, da ich sowieso die komplette Form und alle ihre Controlls deaktiviere solange gelesen wird. Mir geht es letztendlich nur um die Animationen der Form (Ladebalken, Fortschrittsanzeige, Statusmeldungen etc.) die halt ge-updated werden sollen solange gelesen wird (weil wenn sich da gar nichts mehr tut denken die User halt schnell dass das Programm eingefroren ist).


Log in to reply