Datenbank und Threading

  • VB.NET

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von vb-xperiment.

    Datenbank und Threading

    Hallo Community,

    bräuchte mal einen Denkanstoß oder Tip.

    Ausgangssituation:

    In einer Form gibt der User seine Suchkriterien ein und startet eine Datenbanksuche per Button.

    In meinem Modul ist dann die Sub die diese Datenbankabfrage ausführt und die Ergebnisse in einen ListView in der User-Form ablegt. Von da kann der User dann was auswählen und bearbeiten ....

    Bis hierhin funzt alles prima.

    Problem:

    Die Userform friert während der DB-Suche (weit mehr als 300tsnd Datensätze) ein. Dies leuchtet mir auch ein.

    Da manche User (darunter ich auch) sehr ungehalten reagieren wenn ein Programm einfach was macht und für 30 Sekunden "tot" ist, möchte ich während der DB-Abfrage dem User irgendwie mitteilen was gemacht wird.

    Eine ProgressBar z.B. hab ich schon ausprobiert, da ich aber im Vorfeld nicht weiß wieviele Daten selektiert werden, kann ich mit der ProgressBar.Value nicht wirklich arbeiten (im ersten Versuch bin ich übers Ziel hinausgeschossen = Indexverletzung :P ).

    Ich habe gelesen dass man solche Aufgaben in Threads packt. Allerdings habe ich damit noch nie gearbeitet und meine Versuche sind kläglich gescheitert:

    Meine DB-Sub erwartet Parameter. Wenn ich diese als Thread starten will geht das nicht weil ich in bei AdressOf(namederdbsub) keine Argumente übergeben kann. Auch eine Schweinehund-Sub die lediglich die DB-Sub mit Parametern startet ( ;( Verzweiflung pur) geht nicht.

    Kann mir jemand einen Stupser geben?
    *stups*

    vb-xperiment schrieb:

    und die Ergebnisse in einen ListView in der User-Form ablegt
    Nimm lieber ein DatagridView, da flexibler. Ausserdem wäre DataBinding hier sehr sinnvoll.

    vb-xperiment schrieb:

    Eine ProgressBar z.B. hab ich schon ausprobiert
    Setze in Deiner Progressbar den Style auf 'Marquee', dann erhältst Du einen hin und her laufenden grünen Balken.

    vb-xperiment schrieb:

    Wenn ich diese als Thread starten will geht das nicht weil ich in bei AdressOf(namederdbsub) keine Argumente übergeben kann.
    Einem Thread kannst Du sehr einfach Argumente übergeben: -> Vorgang asynchron ausführen Post #4
    Wenn Du in einem Thread Updates auf Controls machen möchtest, so beachte Invoke.
    Zunächst Danke für die Antwort.

    Habe nun folgendes Konstrukt in der Userform:
    Dim tr AsNew System.Threading.Thread(Sub() namederabfragesub(param1, param2, param3, namederListView))
    tr.Start()



    In der Sub selber behandle ich das Füllen der ListView mit

    ListView.Invoke(
    Sub() ListView.Items.Add(reader("blabla")))

    Das klappt ganz gut, meine Form friert nicht ein, ich sehe auch dass die ListView gefüllt wird und mittels ListView.Refresh() kann ich dem User visualisieren dass das Programm arbeitet.

    Irgendwie scheint aber das Programm nicht zu merken dass der Thread fertig ist, denn die auf tr.Start() folgenden Aktionen werden nicht ausgeführt?? Endet mein Thread nicht, oder reagiere ich nicht drauf??? (Bitte um Nachsicht, Threads sind für mich ein Buch mit 7 Siegeln)
    Ein Licht geht auf!!

    Gibt es ne Möglichkeit auf das Ende des Threads zu warten und erst dann fortzusetzen?

    Join und Suspend macht ja (wenn ich das richtig kapiert hab) keinen Sinn und mit isAlive sehe ich ja nur ob der noch arbeitet...

    vb-xperiment schrieb:

    Ich glaube das ist nicht das was ich suche.
    Das glaube ich auch nicht (Application.DoEvents) ist 'böse'.

    Wenn Du blockst bis der Thread fertig ist, friert die Form ein, das hast Du ja schon gemerkt. Du kannst aber im GUI-Thread mit der Arbeit fortfahren, indem Du entweder
    a) auf ein Event reagierst
    b) die Folgeverarbeitung direkt anstösst
    Meist sind hier Aufräumarbeiten wie das Aktivieren von Controls o.ä. nötig.
    "Beispiel"

    VB.NET-Quellcode

    1. Imports System.Threading
    2. Public Class frmControl
    3. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    4. Vorbereiten()
    5. ' Thread anlegen: wichtig .isBackground=true, da dann der Thread endet wenn Form geschlossen wird
    6. Dim t As New Thread(Sub() Verarbeiten(10)) With {.IsBackground = True}
    7. t.Start()
    8. End Sub
    9. ' Verarbeitung separat vom GUI Thread
    10. Sub Verarbeiten(ByVal count As Integer)
    11. For i = 0 To count
    12. Me.Invoke(Sub() Me.Text = String.Format("{0:HHH.mm.ss}", DateTime.Now))
    13. Thread.Sleep(1000)
    14. Next i
    15. ' im GUI Thread ´weitermachen und thread beenden
    16. Me.BeginInvoke(Sub() Weitermachen())
    17. End Sub
    18. ' deaktivert Button und aktiviert Progressbar
    19. Sub Vorbereiten()
    20. ProgressBar1.Style = ProgressBarStyle.Marquee
    21. Button1.Enabled = False
    22. End Sub
    23. ' aktivert Button und deaktiviert Progressbar
    24. Sub Weitermachen()
    25. Button1.Enabled = True
    26. ProgressBar1.Style = ProgressBarStyle.Blocks
    27. End Sub
    28. End Class

    Habe nun gemäß des Beispiels in die Abfrage-Sub (die ich ja in einem Thread starte) folgendes eingetragen (ans Ende):

    Userform.BeginInvoke(Sub() Userform.eineAndereSub())

    Diese "eineAndereSub()" wäre der nächste Schritt im Programm nach Ende des Threads. Diese wertet Daten der ListView aus (z.B. items.count etc.) und trägt diese in TextBoxen ein.

    Leider kommt jetzt der Fehler:

    Invoke oder BeginInvoke kann für ein Steuerelement erst aufgerufen werden, wenn das Fensterhandle erstellt wurde.

    Zum Verständnis: abfragesub steht im Modul, alles andere in der Userform.

    Was mache ich hier falsch?

    vb-xperiment schrieb:

    Was mache ich hier falsch?

    Eigentlich nichts, nur VB hat hier prinzipiell ein Problem, da es beim Ansprechen von Form Klassen immer versucht eine Instanz im Hintergrund anzulegen (falls nicht vorhanden).

    Mach folgendes: übergib die Instanz Deiner UserForm in der Parameter-Liste des Threads:
    - Definition im Modul: Sub abfragesub(frm as UserForm,....)
    - Aufruf aus UserForm:abfragesub(Me,....)
    Und das Invoke im Thread dann bitte generell auf frm.