Threads und mehrere Forms

  • VB.NET

Es gibt 20 Antworten in diesem Thema. Der letzte Beitrag () ist von Ringelnatz98.

    Threads und mehrere Forms

    Ich habe einen TCP-Server programmiert, der natürlich in einem eigenen Thread laufen muss. Nun möchte ich auf Knopfdruck eine weitere Form aufrufen lassen. Das Problem ist, dass jede Form die ich erstelle, sich beim öffnen direkt aufhängt. Ich vermute mal, dass das irgendwie mit dem Thread zusammenhängt.
    Wie kann ich das Problem lösen?
    okay.. was steht den im form_load event der form?
    hast du irgendwelche merkwürdigen controls auf dieser platziert oder diese evtl so "vercoded", dass eine endlosschleife entstehen könnte (z.b. im texbox_textchange event steht, dass der text = "Name eingeben" sein soll)?

    lg
    Habe jetzt nochmal ein wenig rumprobiert. Also wen ich die Form aus einer normalen Sub heraus öffnen lasse funktioniert es tatsächlich.
    Scheinbar hängt sich die Form dann auf wenn sie in einer Sub aufgerufen wird, welche vom Thread durchlaufen wird.
    Ich war auch erst verwundert, ich habe nämlich noch überhaupt keinen Code in der neuen Form.
    Das heißt ich darf die Form nicht aus dem Thread heraus aufrufen...
    Als ich mit Threads anfing hatte ich mir das irgendwie einfacher vorgestellt... :wacko:

    /EDIT:
    Habe gerade folendes Thema gefunden: [VB.NET] Form in anderen Thread öffnen
    Mein Problem ist aber dass ich weiterhin zwischen Hauptform und zweiter Form kommunizieren können muss.
    Gibt es nicht einen anderen Weg?

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

    Ringelnatz98 schrieb:

    Mein Problem ist aber dass ich weiterhin zwischen Hauptform und zweiter Form kommunizieren können muss.

    Hinter jeder Form ( und jedem Control) steckt ein Window des Betriebsystems. Um miteinander kommunizieren zu können sollten sie in dem gleichen Thread laufen / eröffnet werden.

    Insofern: warum eröffnest Du neue Forms in einem separaten Thread ? und wenn Du das schon aus irgendwelchem obskuren Gründen tun musst, warum eröffnest Du sie dann nicht auf dem gemeinsamen GUI-Thread ?

    Kangaroo schrieb:

    warum eröffnest Du neue Forms in einem separaten Thread ?

    Da ich die Form bzw. die Sub die die Form aufruft momentan vom Thread aus starte, der auf TCP-Verbindungen wartet, hängt sich die neue Form direkt auf, zumindet glaube ich dass es daran liegt. Deshalb versuche ich, die Form in einem anderen Thread zu starten, der nicht blockiert ist. Kenn mich nicht so mit Threads aus, wenns ne einfachere Lösung gibt, her damit, Google lässt mich mit diesem Thema ziemlich im Stich!
    Vielleicht machst Du mal ein gaaanz einfaches Besipiel, damit mal klar wird was Du eigentlich tust und erreichen möchtest.

    Auf jedem Fall solltest Du definitiv immer vermeiden den Main (GUI) Thread in irgendeinerwewise zu blocken. Bei diesem muss die 'Message-Pump' weiter gehen ....
    Nein nein, ich blockiere nicht den Hauptthread, das ist ein eigener
    Thread, aber von da aus wird halt die Form geladen, was ich zu umgehen
    versuche. Hier mal mein zusammengefasster Code:

    VB.NET-Quellcode

    1. Private Sub ListenToConnection(ByVal con As Connection)
    2. Do
    3. Try
    4. Dim tmp As String = con.streamr.ReadLine
    5. Verarbeite(tmp, con.nick) ' hierdrin wird die Form aufgerufen
    6. Catch
    7. '...
    8. End Try
    9. Loop
    10. End Sub
    11. Private Sub Verarbeite(ByVal s As String, ByVal nick As String)
    12. Dim form As New form_xy
    13. form.Show()
    14. End Sub

    In
    Verarbeite() wird form aufgerufen, die sich allerdings wie gesagt
    direkt aufhängt. ListenToConnection läuft in einem eigenem Thread.

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

    Ringelnatz98 schrieb:

    Verarbeite() wird form aufgerufen, die sich allerdings wie gesagt direkt aufhängt. ListenToConnection läuft in einem eigenem Thread.

    Das einfachste wäre die Instanz der Form ausserhalb des Threads anzulegen. Innerhalb des Threads kannst Du sie dann ( per Invoke ) anzeigen.

    Oder wenn das nicht geht, z.B. weil es mehrere Threads/Forms geben kann: pro Thread eine Form per Invoke direkt auf dem GUI Thread anlegen.

    Allerdings sollte man bei solchen Konstellationen , wo Threads auf GUI-Elemente zugreifen, immer das Programm-Design genau prüfen. Evtl. ist res besser den Thread unabhängig von der Präsentation arbeiten zu lassen und Ergebnisse nur per Event mitzuteilen.

    Kangaroo schrieb:

    Das einfachste wäre die Instanz der Form ausserhalb des Threads anzulegen. Innerhalb des Threads kannst Du sie dann ( per Invoke ) anzeigen.

    Ok, habe folgendes versucht:

    VB.NET-Quellcode

    1. Dim form As New form_xy
    2. Private Delegate Sub OpenFormHandler()
    3. Private Sub OpenForm()
    4. form.Show()
    5. End Sub
    6. Private Sub Verarbeite(ByVal s As String, ByVal nick As String)
    7. form.MacheDiesUndDas(s)
    8. Me.form.Invoke(New OpenFormHandler(AddressOf OpenForm))
    9. End Sub

    Aber es öffnet sich nix und merkwürdiger Weise verschwinden auch alle TCP-Clients die ich in meiner Liste habe (die senden Daten die ich in an die zweite Form übergeben will).

    Ringelnatz98 schrieb:

    Aber es öffnet sich nix und merkwürdiger Weise verschwinden auch alle TCP-Clients die ich in meiner Liste habe (die senden Daten die ich in an die zweite Form übergeben will).

    Ich sehe nichts was mit Threading zu tun hat, aber so sollte es sowieso nicht ohne Fehlermeldung laufen. Du verwendest Me.form.Invoke zu einem Zeitpunkt, wo für dieses noch kein WindowHandle exisitiert. ( New erzwingt kein Handle, erst die Methode Show). Nimm zum Invoken daher eine existierende Form.

    Weiterhin gibts noch ein paar mehr Unsauberkeiten:

    VB.NET-Quellcode

    1. Dim form As New form_xy ' nicht Klassennamen verwenden, nimm lieber frm als Namen
    2. Private Delegate Sub OpenFormHandler() ' zwar richtig, aber diesen Handler gibt es schon mit Namen MethodInvoker
    3. Private Sub OpenForm()
    4. form.Show()
    5. End Sub
    6. Private Sub Verarbeite(ByVal s As String, ByVal nick As String)
    7. form.MacheDiesUndDas(s) ' gefährlich: geht schief wenn die Methode auf irgendwelche controls der form_xy zugreift
    8. Me.form.Invoke(New OpenFormHandler(AddressOf OpenForm)) ' sollte Exception werfen, da noch kein Handle da ist.
    9. ' Verwende zum Invoken ein Control welches schon angezeigt wird , also ein Handle besitzt.
    10. ' zum Beispiel Me.Invoke oder Application.forms(0).Invoke
    11. End Sub
    Vielen Dank, das hat mich schonmal viel weiter gebracht!
    Also, Die Form form heißt in wirklichkeit anders, ebenso die OpenFormHandler(), die habe ich nur zur besseren Übersichtlichkeit umbennant.
    Habe den Code wie folgt abgeändert:

    VB.NET-Quellcode

    1. Me.Invoke(New OpenFormHandler(AddressOf OpenForm)

    und das funktioniert schonmal.

    Kangaroo schrieb:

    form.MacheDiesUndDas(s) ' gefährlich: geht schief wenn die Methode auf irgendwelche controls der form_xy zugreift

    Die Method greift in der Tat auf Controls in form_xy zu, allerdings ohne Probleme ?(

    Ringelnatz98 schrieb:

    und das funktioniert schonmal.

    Dann schieben wir mal 2 alternative Möglichkeiten zum Invoken nach. Da Du ja anscheinend Visual Basic 10.0 verwendest, so gehen sehr schön díe anonymen Methoden als Ersatz für einen Delegaten:

    VB.NET-Quellcode

    1. ' deine version
    2. Me.Invoke(New OpenFormHandler(AddressOf OpenForm)
    3. ' alternativ mit MethodInvoker
    4. Me.Invoke(New MethodInvoker(AddressOf OpenForm))
    5. ' alternativ mit anonymer Methode
    6. Me.Invoke(Sub() OpenForm())


    Ringelnatz98 schrieb:

    Die Method greift in der Tat auf Controls in form_xy zu, allerdings ohne Probleme

    Das sollte nicht möglich sein , wenn Du weder CheckForIllegalCrossThreadCalls = false gesetzt hast oder die Exception irgendwo per Try..Catch abfängst. Auch hier wird ja sonst abseits vom GUI-Thread auf Controls zugegriffen, da Du nicht invoke benutzt.
    Yup, das Catch fängt alles ab: von der Division durch 0 bis zum IllegalCrossThreadCall. So kannst Du wirklich jeden Runtime Fehler in Deinem Code begehen und merkst es nie.

    Man versucht erwartete Handlungen ('User meldet sich ab') immer ohne Exception abzufangen. Ist es einmal unumgänglich so fängt man gezielt die Exception für diesen Fall ab, aber verwendet nie diesen General-Pardon.

    Ringelnatz98 schrieb:

    Dort steht es so und ich weiß auch gar nicht wie ich es anders machen kann.

    Da hast Du Recht, aber in Kevins Code können auch nicht so schrecklich viele andere Ursachen für eine Exception ausser der User-Abmeldung vorliegen. Dennoch ist es auch dort unsauber.

    Mir gehts auch nur darum dass Deine form.machirgendwas Methode auch invoked werden müsste, sobald sie auf ihre Controls zugreift.

    Obwohl Du ja sagst es funktioniert jetzt sauber, testen kannst ja nur Du es ....