Separater Thread, Form friert trotzdem

  • WPF

Es gibt 28 Antworten in diesem Thema. Der letzte Beitrag () ist von Dksksm.

    Separater Thread, Form friert trotzdem

    Hi,

    ich habe eine separaten thread um viel arbeit zu erledigen, leider friert die main form trotzdem. ich finde den fehler gerade nicht -.- Umgebung ist WPF falls das ne rolle spielt...

    VB.NET-Quellcode

    1. Public Delegate Sub SucheFrage()
    2. Private Sub Btn_SucheFrageCreateDatabase_Click(sender As Object, e As RoutedEventArgs) Handles Btn_SucheFrageCreateDatabase.Click
    3. Dim t As New Thread(AddressOf CreateDatabase)
    4. t.Start()
    5. End Sub
    6. Sub CreateDatabase()
    7. Dispatcher.BeginInvoke(New SucheFrage(AddressOf SucheCreateDatabase))
    8. End Sub
    9. Sub SucheCreateDatabase()
    10. 'heavy work'
    11. End sub


    hab ich irgendwo was vergessen? Oder ist die arbeit zu heavy? :D
    @r0tzi Klar.
    Du invokst die Arbeit sofort in den MainThread.
    Probierma

    VB.NET-Quellcode

    1. Public Delegate Sub SucheFrage()
    2. Private Sub Btn_SucheFrageCreateDatabase_Click(sender As Object, e As RoutedEventArgs) Handles Btn_SucheFrageCreateDatabase.Click
    3. Dim t As New Thread(AddressOf CreateDatabase)
    4. t.Start()
    5. End Sub
    6. Sub CreateDatabase()
    7. 'heavy work'
    8. End sub

    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!
    @r0tzi Da ist doch ganz gewiss noch weiterer Code im Spiel ???
    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!
    klaro.. heavy work ist das:

    VB.NET-Quellcode

    1. If Me.Connection.State = ConnectionState.Open Then
    2. Else
    3. Me.Connection.Open()
    4. End If
    5. Prog_SucheFrage.Value = 0
    6. DT_SucheFrage.Clear()
    7. DT_SucheFrage.Rows.Clear()
    8. Dim bytesLoad As Byte()
    9. Dim Command As New SqlCommand("SELECT [Nr],[Tag],[Ticket],[Bereich] FROM dbo.[WIR_TicketsPublic]") With {.Connection = Me.Connection}
    10. Dim adapter As New SqlDataAdapter(Command)
    11. adapter.Fill(DT_SucheFrage)
    12. For i = 0 To DT_SucheFrage.Rows.Count - 1
    13. Prog_SucheFrage.Maximum = DT_SucheFrage.Rows.Count
    14. bytesLoad = CType(DT_SucheFrage.Rows(i).Item("Ticket"), Byte())
    15. Using fs As FileStream = New FileStream("C:\Temp\WIRSuche.XAML", FileMode.Append)
    16. Dim len As Long = bytesLoad.Length
    17. While fs.Position < bytesLoad.Length
    18. fs.Write(bytesLoad, 0, bytesLoad.Length)
    19. End While
    20. fs.Close()
    21. fs.Dispose()
    22. My.Computer.FileSystem.WriteAllBytes("C:\Temp\WIRSuche.XAML", bytesLoad, False)
    23. If File.Exists("C:\Temp\WIRSuche.XAML") Then
    24. Dim t As TextRange = New TextRange(RTF_sucheFrage.Document.ContentStart, RTF_sucheFrage.Document.ContentEnd)
    25. Dim File1 As FileStream = New FileStream("C:\Temp\WIRSuche.XAML", FileMode.Open)
    26. t.Load(File1, System.Windows.DataFormats.XamlPackage)
    27. DT_SucheFrage.Rows(i).Item("Strings") = UCase(t.Text)
    28. File1.Close()
    29. File.Delete("C:\Temp\WIRSuche.XAML")
    30. DT_SucheFrage.Rows(i).Item("Ticket") = System.Text.Encoding.Unicode.GetBytes("")
    31. End If
    32. End Using
    33. Prog_SucheFrage.Value = i
    34. Debug.Print(i.ToString)
    35. Next
    36. If File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) & "\AppData\Local\WIR\Database.xml") Then
    37. File.Delete(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) & "\AppData\Local\WIR\Database.xml")
    38. Else
    39. DT_SucheFrage.WriteXml(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) & "\AppData\Local\WIR\Database.xml", True)
    40. End If
    41. Prog_SucheFrage.Value = 0
    42. Me.RTF_sucheFrage.Document.Blocks.Clear()


    ich hohle bytes aus SQL, wandel die um in Strings .. frag nicht warum den umweg über XAML... per memorystream dauerte es 5x so lange... :D
    Dann mach doch mal bitte erstmal ne sinnvolle Zeitmessung, um herauszubekommen, was von dem ganzen Code, der da abläuft, der zeitfressende ist. Da passiert eh viel zu viel unterschiedliches in einer Sub. Sobald Du den Code sinnvoll aufgeteilt hast, erkennst Du sicherlich viel besser, was zeitaufwendig ist und somit asynchron ablaufen soll und was ganz normal im GUI-Thread ablaufen kann, weil es nicht sonderlich viel Zeit frisst.
    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.
    Hi,

    also ich möchte, dass die Datenbank bei programmstart aktualisiert wird. dies dauert ca~ 10 sekunden. Läuft es im Mainthread, bleibt das programm 10 sekunden stehen.
    ich möchte es deshalb in einem separaten thread, dass der user ausser einer progressbar, nichts bemerkt/sieht.
    Da sich die Ladezeiten verlängern kann in zukunft, möchte ich es eben nebenbei laden lassen. Async habe ich auch probiert, funktioniert ebenfalls nicht.
    Ich habe nun alles gepostet, was passiert -> Form bleibt gefrorern bis er fertig ist und das hätte ich gern nebenbei.

    @VaporiZed es passiert eigentlich "nur" download von SQL und alle bytes werden in strings umgewandelt und in den datatable geschrieben. wie gesagt, den umweg über xaml, da schneller. ->siehe Inhalt von RTB als ByteArray speichern
    Vorrausgesetzt dass dein Code, den du bisher gepostet hast, alles ist, habe ich hier mal für dich einen Async/Await Ansatz. Das einzige was du jetzt noch machen musst, ist dein Click-Event als Async deklarieren und dann diese Funktion Awaiten. Ich habe jetzt einfach mal aus meiner Erfahrung heraus zum einen das Befüllen des DataAdapters ausgelagert, zum anderen die ganze for-Schleife, da du u.U. hier etliche Schreibvorgänge auf die Platte hast. Hätte ich auch in einen einzigen Task auslagern können, jedoch denke ich, gehören Datenbeschaffung, und Datenverarbeitung in separate Funktionen.

    Was deine Progressbar angeht, musst du nun entweder aus dem Task heraus Invoken, oder dich mit der Klasse Progress bzw. IProgress auseinandersetzen.

    VB.NET Converter

    VB.NET-Quellcode

    1. Private Async Function SucheCreateDatabase() As Task
    2. If Me.Connection.State <> ConnectionState.Open Then
    3. Me.Connection.Open()
    4. End If
    5. Prog_SucheFrage.Value = 0
    6. DT_SucheFrage.Clear()
    7. DT_SucheFrage.Rows.Clear()
    8. Dim bytesLoad As Byte()
    9. Dim Command As SqlCommand = New SqlCommand("SELECT [Nr],[Tag],[Ticket],[Bereich] FROM dbo.[WIR_TicketsPublic]") With {
    10. .Connection = Me.Connection
    11. }
    12. Dim adapter As SqlDataAdapter = New SqlDataAdapter(Command)
    13. Await Task.Run(Function() adapter.Fill(DT_SucheFrage))
    14. Await Task.Run(Function()
    15. For i = 0 To DT_SucheFrage.Rows.Count - 1
    16. Prog_SucheFrage.Maximum = DT_SucheFrage.Rows.Count
    17. bytesLoad = CType(DT_SucheFrage.Rows(i).Item("Ticket"), Byte())
    18. Using fs As FileStream = New FileStream("C:\Temp\WIRSuche.XAML", FileMode.Append)
    19. Dim len As Long = bytesLoad.Length
    20. While fs.Position < bytesLoad.Length
    21. fs.Write(bytesLoad, 0, bytesLoad.Length)
    22. End While
    23. fs.Close()
    24. fs.Dispose()
    25. My.Computer.FileSystem.WriteAllBytes("C:\Temp\WIRSuche.XAML", bytesLoad, False)
    26. If File.Exists("C:\Temp\WIRSuche.XAML") Then
    27. Dim t As TextRange = New TextRange(RTF_sucheFrage.Document.ContentStart, RTF_sucheFrage.Document.ContentEnd)
    28. Dim File1 As FileStream = New FileStream("C:\Temp\WIRSuche.XAML", FileMode.Open)
    29. t.Load(File1, System.Windows.DataFormats.XamlPackage)
    30. DT_SucheFrage.Rows(i).Item("Strings") = UCase(t.Text)
    31. File1.Close()
    32. File.Delete("C:\Temp\WIRSuche.XAML")
    33. DT_SucheFrage.Rows(i).Item("Ticket") = System.Text.Encoding.Unicode.GetBytes("")
    34. End If
    35. End Using
    36. Debug.Print(i.ToString())
    37. Next
    38. End Function)
    39. If File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) & "\AppData\Local\WIR\Database.xml") Then
    40. File.Delete(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) & "\AppData\Local\WIR\Database.xml")
    41. Else
    42. DT_SucheFrage.WriteXml(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) & "\AppData\Local\WIR\Database.xml", True)
    43. End If
    44. Prog_SucheFrage.Value = 0
    45. Me.RTF_sucheFrage.Document.Blocks.Clear()
    46. End Function
    C# Original

    C#-Quellcode

    1. private async Task LoadData()
    2. {
    3. if (this.Connection.State != ConnectionState.Open)
    4. {
    5. this.Connection.Open();
    6. }
    7. Prog_SucheFrage.Value = 0;
    8. DT_SucheFrage.Clear();
    9. DT_SucheFrage.Rows.Clear();
    10. byte[] bytesLoad;
    11. SqlCommand Command = new SqlCommand("SELECT [Nr],[Tag],[Ticket],[Bereich] FROM dbo.[WIR_TicketsPublic]") { Connection = this.Connection };
    12. SqlDataAdapter adapter = new SqlDataAdapter(Command);
    13. await Task.Run(()=> adapter.Fill(DT_SucheFrage));
    14. await Task.Run(()=>
    15. {
    16. for (var i = 0; i <= DT_SucheFrage.Rows.Count - 1; i++)
    17. {
    18. Prog_SucheFrage.Maximum = DT_SucheFrage.Rows.Count;
    19. bytesLoad = (byte[])DT_SucheFrage.Rows(i).Item("Ticket");
    20. using (FileStream fs = new FileStream(@"C:\Temp\WIRSuche.XAML", FileMode.Append))
    21. {
    22. long len = bytesLoad.Length;
    23. while (fs.Position < bytesLoad.Length)
    24. fs.Write(bytesLoad, 0, bytesLoad.Length);
    25. fs.Close();
    26. fs.Dispose();
    27. My.Computer.FileSystem.WriteAllBytes(@"C:\Temp\WIRSuche.XAML", bytesLoad, false);
    28. if (File.Exists(@"C:\Temp\WIRSuche.XAML"))
    29. {
    30. TextRange t = new TextRange(RTF_sucheFrage.Document.ContentStart, RTF_sucheFrage.Document.ContentEnd);
    31. FileStream File1 = new FileStream(@"C:\Temp\WIRSuche.XAML", FileMode.Open);
    32. t.Load(File1, System.Windows.DataFormats.XamlPackage);
    33. DT_SucheFrage.Rows(i).Item("Strings") = UCase(t.Text);
    34. File1.Close();
    35. File.Delete(@"C:\Temp\WIRSuche.XAML");
    36. DT_SucheFrage.Rows(i).Item("Ticket") = System.Text.Encoding.Unicode.GetBytes("");
    37. }
    38. }
    39. Debug.Print(i.ToString());
    40. }
    41. });
    42. if (File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\AppData\Local\WIR\Database.xml"))
    43. File.Delete(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\AppData\Local\WIR\Database.xml");
    44. else
    45. DT_SucheFrage.WriteXml(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\AppData\Local\WIR\Database.xml", true);
    46. Prog_SucheFrage.Value = 0;
    47. this.RTF_sucheFrage.Document.Blocks.Clear();
    48. }



    Edit: Mir ist leider erst im Nachhinein eingefallen, dass der FileStream ja Funktionen wie WriteAsync besitzt, die man Awaiten kann. Das wäre natürlich eine weitaus elegantere Lösung, als einfach die gesamte For-Schleife in einen Task zu packen, und würde vmtl. auch direkt das Problem mit der Progressbar lösen.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „EaranMaleasi“ ()

    @EaranMaleasi teste ich auch mal moment

    ABER ICH HABE DIE LÖSUNG DURCH ZUFALL GEFUNDEN

    wenn ich das hinzufüge:

    VB.NET-Quellcode

    1. Public Async Function StautsSucheLong() As Task
    2. Await Task.Delay(1)
    3. Me.Prog_SucheFrage.Value += 1
    4. End Function


    und in der For schleife

    VB.NET-Quellcode

    1. Await StautsSucheLong()


    DANN funktioniert es!

    kann mir das jemand erklären? Bleibe ich NUR im thread, friert die Form, gehe ich mit einer Async methode raus, GEHT ES!

    Edit:
    @EaranMaleasi wieder fehlermeldung crossthreads bei update der progressbar.

    r0tzi schrieb:

    Edit: @EaranMaleasi wieder fehlermeldung crossthreads bei update der progressbar.

    EaranMaleasi schrieb:

    Was deine Progressbar angeht, musst du nun entweder aus dem Task heraus Invoken, oder dich mit der Klasse Progress bzw. IProgress auseinandersetzen.


    Könntest du uns deinen jetzigen Code, mit deiner "Lösung" nochmal posten? ICh hab keine Ahnung, ob du nun meinen Vorschlag verwendest, oder deinen bisherigen Code modifiziert hast, oder wie auch immer. Dann kann ich dir erklären, was hier vor sich geht.
    klar

    VB.NET-Quellcode

    1. Public Delegate Sub SucheFrage()
    2. Sub Start()
    3. Dim t As New Thread(AddressOf CreateDatabase)
    4. t.Start()
    5. End sub
    6. Sub CreateDatabase()
    7. Dispatcher.BeginInvoke(New SucheFrage(AddressOf SucheCreateDatabase))
    8. End Sub
    9. Public Async Sub SucheCreateDatabase()
    10. Await Task.Delay(1)
    11. If Me.Connection.State = ConnectionState.Open Then
    12. Else
    13. Me.Connection.Open()
    14. End If
    15. Prog_SucheFrage.Value = 0
    16. DT_SucheFrage.Clear()
    17. DT_SucheFrage.Rows.Clear()
    18. Dim bytesLoad As Byte()
    19. Dim Command As New SqlCommand("SELECT [Nr],[Tag],[Ticket],[Bereich] FROM dbo.[WIR_TicketsPublic]") With {.Connection = Me.Connection}
    20. Dim adapter As New SqlDataAdapter(Command)
    21. adapter.Fill(DT_SucheFrage)
    22. Me.Prog_SucheFrage.Maximum = DT_SucheFrage.Rows.Count - 1
    23. For i = 0 To DT_SucheFrage.Rows.Count - 1
    24. Await StautsSucheLong()
    25. If BolstopLoadDatabase = True Then
    26. Me.Prog_SucheFrage.Value = 0
    27. Exit Sub
    28. End If
    29. bytesLoad = CType(DT_SucheFrage.Rows(i).Item("Ticket"), Byte())
    30. Using fs As FileStream = New FileStream("C:\Temp\WIRSuche.XAML", FileMode.Append)
    31. Dim len As Long = bytesLoad.Length
    32. While fs.Position < bytesLoad.Length
    33. fs.Write(bytesLoad, 0, bytesLoad.Length)
    34. End While
    35. fs.Close()
    36. fs.Dispose()
    37. My.Computer.FileSystem.WriteAllBytes("C:\Temp\WIRSuche.XAML", bytesLoad, False)
    38. If File.Exists("C:\Temp\WIRSuche.XAML") Then
    39. Dim t As TextRange = New TextRange(RTF_sucheFrage.Document.ContentStart, RTF_sucheFrage.Document.ContentEnd)
    40. Dim File1 As FileStream = New FileStream("C:\Temp\WIRSuche.XAML", FileMode.Open)
    41. t.Load(File1, System.Windows.DataFormats.XamlPackage)
    42. DT_SucheFrage.Rows(i).Item("Strings") = UCase(t.Text)
    43. File1.Close()
    44. File.Delete("C:\Temp\WIRSuche.XAML")
    45. DT_SucheFrage.Rows(i).Item("Ticket") = System.Text.Encoding.Unicode.GetBytes("")
    46. End If
    47. End Using
    48. Debug.Print(i.ToString)
    49. Next
    50. If File.Exists("C:\Temp\Database.xml") Then
    51. File.Delete("C:\Temp\Database.xml")
    52. Else
    53. DT_SucheFrage.WriteXml("C:\Temp\Database.xml", True)
    54. End If
    55. Prog_SucheFrage.Value = 0
    56. Me.RTF_sucheFrage.Document.Blocks.Clear()
    57. End Sub
    58. Public Async Function StautsSucheLong() As Task
    59. Await Task.Delay(1)
    60. Me.Prog_SucheFrage.Value += 1
    61. End Function

    Du erstellst nen Thread, in dem du den Dispatcher darum bittest, SucheCreateDatabase() im Hauptthread auszuführen, um dann dort drin für jede for Iteration 1ms asynchron zu warten... habe ich das richtig verstanden?

    Wenn nun dein Code von SucheCreateDatabase() an den Punkt kommt, an dem StautsSucheLong() aufgerufen wird, ist alles bisher komplett synchron abgelaufen. In deiner neuen Funktion, wartest du nun ungefähr 1ms asynchron. Dies bedeutet, dass du einen Task erstellst, den für ungefähr 1ms laufen lässt, und dann diesen beendest, und zu deinem Hauptthread zurückkerst. Unglücklicherweise ist diese 1ms genügend Zeit für die GUI (solange du wartest, wird die Kontrolle an die GUI zurückgegeben) um sich zu updaten und genügend Messages zu verarbeiten, und deine Anwendung für das System nicht eingefroren wirkt.

    Am besten mach nun folgendes:
    Lösch die Funktionen, die den Thread starten, und den Dispatcher aufrufen restlos. Wandle deine Public Async Sub SucheCreateDatabase() in eine Public Async Function SucheCreateDatabase() As Task um, und rufst diese Funktion, von wo auch immer du sie aufrufst, mit Await auf. Sollte dies nicht gehen, musst du solange die aufrufenden Subs und Functions in Async Function umwandeln, bis du bei einem Event, oder der Main-Methode angekommen bist. Innerhalb der Funktion rufst du nun asynchrone Methoden wie die WriteAsync Methode des FileStreams auf, und Awaitest diese. Dort wo nun StatusSucheLong() aufgerufen wird, kannst du direkt deine ProgressBar Inkrementieren, ohne Task.Delay()
    Hi, Danke für die Antwort, benötige noch ein beispiel zu
    Innerhalb der Funktion rufst du nun asynchrone Methoden wie die WriteAsync Methode des FileStreams auf, und Awaitest diese. Dort wo nun StatusSucheLong() aufgerufen wird, kannst du direkt deine ProgressBar Inkrementieren, ohne Task.Delay()



    Bei

    Quellcode

    1. Prog_SucheFrage.Maximum = DT_SucheFrage.Rows.Count
    sagt er mir z.b. wieder crossthreads, wie muss das heissen?
    @r0tzi Du greifst auf ein Control aus einem anderen Thread zu, Du musst die Abfrage invoken.
    docs.microsoft.com/de-de/dotne…oke?view=netframework-4.8
    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!
    @r0tzi mal als Anhaltspunkt:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    3. Label1.Text = ReadTextBoxText(TextBox1)
    4. End Sub
    5. Public Shared Function ReadTextBoxText(ByVal varControl As TextBox) As String
    6. If varControl.InvokeRequired Then
    7. ' ruft sich selbst im GUI-Thread auf
    8. Return Convert.ToString(varControl.Invoke(New Func(Of String)(Function() Form1.ReadTextBoxText(varControl))))
    9. End If
    10. Return varControl.Text
    11. End Function
    12. End Class
    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!

    r0tzi schrieb:

    liegt es daran dass es WPF ist?
    Diese Information gehört in den Titel und dieser Theread in ein anderes Unterforum.
    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!
    @r0tzi gibt es einen Grund warum du hier Postest und nicht im WPF Bereich?

    Ich werde das mal verschieben!

    *Verschoben*
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    solangsam versteh ich gar nichts mehr... hier ein thread den ich in WPF auf gemacht habe, wo ich die hucke voll bekommen habe weils keinen direkten bezug zu WPF hatte...
    Datatable nur 4500 Rows?

    hier, was eig auch kein direkten bezug zu WPF hat, da es eigentlich um separate threads geht, wird nun in WPF verschoben O_o

    Diese Information gehört in den Titel und dieser Theread in ein anderes Unterforum.

    das label war/ist doch WPF ?!