Anwendung ist sehr viel langsamer bei Fortschrittsanzeige

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

Es gibt 44 Antworten in diesem Thema. Der letzte Beitrag () ist von jvbsl.

    Anwendung ist sehr viel langsamer bei Fortschrittsanzeige

    Hallo!

    Habe ein Programm (VB2012) geschrieben, bei dem ich einen Startordner und eine Suchmaske angebe. Nun wird der Ordner (inklusive Unterverzeichnisse) der Suchmaske entsprechend durchsucht und die gefundenen Dateien aufgelistet.

    Das Programm arbeitet zügig. Wenn ich nun aber eine Fortschrittsanzeige egal ob mit Progressbar ( Value +1 bei jeder Datei) oder ein Label, welches den aktuellen Dateinamen anzeigt, wird die Performance deutlich schlechter.

    Gibt es eine Möglichkeit irgendwie eine Fortschrittsanzeige ohne Geschwindigkeitsverlust hinzubekommen?

    Danke
    Also habe mal einen Timer geschaltet. Ich Durchsuche mein Laufwerk D:\
    Darauf sind knapp 4300 Dateien

    Ohne Label- Anzeige dauert der Vorgang weniger als 1 Sekunde
    Mit Label - Anzeige dauert der Vorgang länger als 3 Sekunden

    Habe jetzt mal nur eine Label - Anzeige der aktuelen Datei gemacht
    Hier der Code des Suchvorgangs: Einzige Änderung ohne Fortschrittsanzeige ist das "Label4.Text = Datei", dann wegfällt

    Quellcode

    1. Private Function durchsuchen(pfad As String) As Boolean
    2. If Not System.IO.Directory.Exists(pfad) Then
    3. MessageBox.Show("Der angegebene Pfad existiert nicht.", "Fehler", _
    4. MessageBoxButtons.OK, MessageBoxIcon.Error)
    5. Return False
    6. End If
    7. Dim attrib As System.IO.FileAttributes
    8. ' Gefundene Dateien zur Liste hinzufügen
    9. Select Case CBx_VersteckteDateien.Checked
    10. Case "True"
    11. For Each Datei As String In My.Computer.FileSystem.GetFiles(pfad)
    12. Label4.Text = Datei
    13. My.Application.DoEvents()
    14. If Ex_Filter.Contains(System.IO.Path.GetExtension(Datei).ToLower) Or _
    15. Ex_Filter.Contains(".*") Then
    16. If System.IO.Path.GetFileNameWithoutExtension(Datei).ToLower.Contains(TBx_Suchbegriff.Text.ToLower) Or _
    17. TBx_Suchbegriff.Text = ("*") Then
    18. Dateiliste.Add(Datei)
    19. End If
    20. End If
    21. Next
    22. Case "False"
    23. For Each Datei As String In My.Computer.FileSystem.GetFiles(pfad)
    24. Label4.Text = Datei
    25. My.Application.DoEvents()
    26. ' Versteckte Dateien nicht anzeigen
    27. attrib = System.IO.File.GetAttributes(Datei)
    28. If ((attrib And IO.FileAttributes.Hidden) <> IO.FileAttributes.Hidden) Then
    29. If Ex_Filter.Contains(System.IO.Path.GetExtension(Datei).ToLower) Or _
    30. Ex_Filter.Contains(".*") Then
    31. If System.IO.Path.GetFileNameWithoutExtension(Datei).ToLower.Contains(TBx_Suchbegriff.Text.ToLower) Or _
    32. TBx_Suchbegriff.Text = ("*") Then
    33. Dateiliste.Add(Datei)
    34. End If
    35. End If
    36. End If
    37. Next
    38. End Select
    39. ' Unterordner durchsuchen
    40. If CBx_Unterordner.Checked = True Then
    41. Dim Aufl_Ordner = System.IO.Directory.GetDirectories(pfad)
    42. Select Case CBx_Verst_ordner.Checked
    43. Case "True"
    44. For Each Unterordner As String In Aufl_Ordner
    45. Try
    46. durchsuchen(Unterordner)
    47. Catch ex As Exception
    48. Fehlerbericht.AppendLine(Unterordner & " - " & ex.Message)
    49. End Try
    50. Next
    51. Case "False"
    52. For Each Unterordner As String In Aufl_Ordner
    53. attrib = System.IO.File.GetAttributes(Unterordner)
    54. If ((attrib And IO.FileAttributes.Hidden) <> IO.FileAttributes.Hidden) Then
    55. Try
    56. durchsuchen(Unterordner)
    57. Catch ex As Exception
    58. Fehlerbericht.AppendLine(Unterordner & " - " & ex.Message)
    59. End Try
    60. End If
    61. Next
    62. End Select
    63. End If
    64. Return Nothing
    65. End Function
    ja natürlich dauert es länger wenn du 4300 mal den text ändern willst von label4...

    mom nein... du durchsuchst ja alle dateien also sind das viel mehr als 4300 :D


    VB.NET-Quellcode

    1. Select Case CBx_VersteckteDateien.Checked
    2. Case "True"
    3. For Each Datei As String In My.Computer.FileSystem.GetFiles(pfad)
    4. My.Application.DoEvents()
    5. If Ex_Filter.Contains(System.IO.Path.GetExtension(Datei).ToLower) Or _
    6. Ex_Filter.Contains(".*") Then
    7. If System.IO.Path.GetFileNameWithoutExtension(Datei).ToLower.Contains(TBx_Suchbegriff.Text.ToLower) Or _
    8. TBx_Suchbegriff.Text = ("*") Then
    9. Dateiliste.Add(Datei)
    10. Label4.Text = Datei
    11. End If
    12. End If
    13. Next


    versuch das mal

    Humax schrieb:

    Label4.Text = Datei
    Jo, wenn da 2000 Ordner durchsucht werden, und 30000 Dateien werden gefunden, dann wird das Label ja 30000 mal getext-ändert, und das ist halt nicht sone ganz schnelle Operation.

    Allerdings kann auch niemand so schnell gucken, wie das arme Label da herumgehopst wird.

    Imo würde reichen, das Label nur alle 0,3 Sekunden zu ändern - dann kann immer noch keiner nix lesen - aber es wackelt fleißig.

    ErfinderDesRades schrieb:

    nur alle 0,3 Sekunden

    @Humax oder alle 1000 Dateien.
    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 würde das hier machen: Du machst anstatt fortschrittsausgabe eine globale Variable in der du den Fortschritt ablegst. Jetzt mach nen Timer den du parallel startest und am Ende wieder beendest. Im TickEvent kannst du den Code für die Ausgabe hintun. Das Intervall ist dann die Aktualisierungsrate an ^^.


    LG Julian
    Hmkay. :|

    MVN050 schrieb:


    mom nein... du durchsuchst ja alle dateien also sind das viel mehr als 4300 :D


    Also ich hab einen Zähler mitlaufen lassen, es sind nur dir 4300 Dateien, aber egal.

    Erst mal Danke für eure Anregungen.

    Ich habe jetzt noch mal etwas getestet und zwar mit meinem Laufwerk F:\, ca. 468000 Dateien; Timer mit Interval 1000 zur Zeitmessung mitlaufen lassen.

    Dauer des Suchvorgangs mit einem Label, dass bei jeder Datei geändert wird: 554 Sekunden
    Dauer des Suchvorgangs mit einer Progressbar, bei der Value an der selben Stelle, also bei jeder Datei um 1 erhöht wird: 292 Sekunden
    Dauer des Suchvorgangs ohne jegliche Fortschrittsanzeige: 31 Sekunden

    Steinigt mich jetzt nicht gleich, aber habe bisher noch nicht wirklich viel Ahnung...
    Hatte evtl. gedacht, dass das Programm, wenn ich die Anzeige in einem Backgroundworker oder in einem Timer mache es schneller gehen könnte, aber wenn ich das jetzt soweit richtig verstanden habe gibt es keine Möglichkeit den Fortschritt "schneller" anzeigen zu lassen, wenn ich bei jeder Datei eine Anzeige mache?!

    Ich habe mich jetzt mal für eine Progressbar entschieden, da die laut meinem Test schneller ist.
    Ich werde vorm Durchsuchen nun die Dateien der zu durchsuchenden Ordner "zählen" und dann je nach dem die Progressbar jeweils bei 1% 5% oder 10% erhöhen.


    miss auch mal, wie lange das Zählen überhaupt dauert.
    Und nimm bitte keinen Backgroundworker - es gibt wesentlich bessere Formen des Threadings - bei .Net4.5 ist der Async-Pattern optimal.
    Damit kann man einen synchronen Vorgangvergleichsweise leicht umbauen in einen asynchronen, ohne die Ablauflogik allzusehr demontieren zu müssen.

    Also du kannst mal deinen synchrongen 31-Sekunden-Code posten, und ich bastel Async ein.
    Oder probierst es selber: Async, Await und Task
    Moin Erfinder. wie gesagt bin nicht so fit, von daher habe ich mich bisher noch nicht mit Async-Pattern etc beschäftigen können. Habe ehrlich gesagt sogar noch nix davon gehört.

    Der Code ist der aus meinem Post Nr.3 nur ohne die Zeilen 14 und 28

    Edit
    Der Code zum Zählen, dauert auch ca. 31 Sekunden

    Quellcode

    1. Private Function Progressbar1_max_ermitteln(pfad As String) As Boolean
    2. If Not System.IO.Directory.Exists(pfad) Then
    3. MessageBox.Show("Der angegebene Pfad existiert nicht.", "Fehler", _
    4. MessageBoxButtons.OK, MessageBoxIcon.Error)
    5. Return False
    6. End If
    7. My.Application.DoEvents()
    8. ProgressBar1.Maximum += My.Computer.FileSystem.GetFiles(pfad).Count
    9. ' Unterordner durchsuchen
    10. If CBx_Unterordner.Checked = True Then
    11. Dim Aufl_Ordner = System.IO.Directory.GetDirectories(pfad)
    12. For Each Unterordner As String In Aufl_Ordner
    13. Try
    14. Progressbar1_max_ermitteln(Unterordner)
    15. Catch ex As Exception
    16. End Try
    17. Next
    18. End If
    19. Return Nothing
    20. End Function

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

    Humax schrieb:

    VB.NET-Quellcode

    1. Dim Aufl_Ordner = System.IO.Directory.GetDirectories(pfad)
    Teste mal dies:

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. Dim fi() = New IO.DirectoryInfo("c:\Temp").GetFiles("*.*", IO.SearchOption.AllDirectories)
    3. MessageBox.Show(fi.Length.ToString)
    4. 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!

    Humax schrieb:

    Der Code zum Zählen, dauert auch ca. 31 Sekunden
    Also allein das Ermitteln des Progressbar-Maximums dauert ebensolange wie die eigentliche Suche?
    Dann lass die PB und das Ermitteln ihres Maximums bitte weg, weil das ist das PB-Gimmick nicht wert, dasses aus 31s Wartezeit 62 Sekunden macht.

    Und jetzt poste den Code der eigentlichen Suche - ohne PB - wenn er 31s dauert.

    ErfinderDesRades schrieb:

    Also allein das Ermitteln des Progressbar-Maximums dauert ebensolange wie die eigentliche Suche?
    - JA

    Der Code zum suchen:

    Quellcode

    1. Private Function durchsuchen(pfad As String) As Boolean
    2. If Not System.IO.Directory.Exists(pfad) Then
    3. MessageBox.Show("Der angegebene Pfad existiert nicht.", "Fehler", _
    4. MessageBoxButtons.OK, MessageBoxIcon.Error)
    5. Return False
    6. End If
    7. Dim attrib As System.IO.FileAttributes
    8. ' Gefundene Dateien zur Liste hinzufügen
    9. Select Case CBx_VersteckteDateien.Checked
    10. Case "True"
    11. For Each Datei As String In My.Computer.FileSystem.GetFiles(pfad)
    12. My.Application.DoEvents()
    13. If Ex_Filter.Contains(System.IO.Path.GetExtension(Datei).ToLower) Or _
    14. Ex_Filter.Contains(".*") Then
    15. If System.IO.Path.GetFileNameWithoutExtension(Datei).ToLower.Contains(TBx_Suchbegriff.Text.ToLower) Or _
    16. TBx_Suchbegriff.Text = ("*") Then
    17. Dateiliste.Add(Datei)
    18. End If
    19. End If
    20. Next
    21. Case "False"
    22. For Each Datei As String In My.Computer.FileSystem.GetFiles(pfad)
    23. My.Application.DoEvents()
    24. ' Versteckte Dateien nicht anzeigen
    25. attrib = System.IO.File.GetAttributes(Datei)
    26. If ((attrib And IO.FileAttributes.Hidden) <> IO.FileAttributes.Hidden) Then
    27. If Ex_Filter.Contains(System.IO.Path.GetExtension(Datei).ToLower) Or _
    28. Ex_Filter.Contains(".*") Then
    29. If System.IO.Path.GetFileNameWithoutExtension(Datei).ToLower.Contains(TBx_Suchbegriff.Text.ToLower) Or _
    30. TBx_Suchbegriff.Text = ("*") Then
    31. Dateiliste.Add(Datei)
    32. End If
    33. End If
    34. End If
    35. Next
    36. End Select
    37. ' Unterordner durchsuchen
    38. If CBx_Unterordner.Checked = True Then
    39. Dim Aufl_Ordner = System.IO.Directory.GetDirectories(pfad)
    40. Select Case CBx_Verst_ordner.Checked
    41. Case "True"
    42. For Each Unterordner As String In Aufl_Ordner
    43. Try
    44. durchsuchen(Unterordner)
    45. Catch ex As Exception
    46. Fehlerbericht.AppendLine(Unterordner & " - " & ex.Message)
    47. End Try
    48. Next
    49. Case "False"
    50. For Each Unterordner As String In Aufl_Ordner
    51. attrib = System.IO.File.GetAttributes(Unterordner)
    52. If ((attrib And IO.FileAttributes.Hidden) <> IO.FileAttributes.Hidden) Then
    53. Try
    54. durchsuchen(Unterordner)
    55. Catch ex As Exception
    56. Fehlerbericht.AppendLine(Unterordner & " - " & ex.Message)
    57. End Try
    58. End If
    59. Next
    60. End Select
    61. End If
    62. Return Nothing
    63. End Function



    Quellcode

    1. ​Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. Dim fi() = New IO.DirectoryInfo("c:\Temp").GetFiles("*.*", IO.SearchOption.AllDirectories)
    3. MessageBox.Show(fi.Length.ToString)
    4. End Sub



    Da bekomme ich eine unauthorized.exception. Z.B. wenn der Startpfad d:\ ist

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

    zunächst mal würde ich vorschlagen, du "lokalisierst" die Methode.

    So wie sie ist, greift sie auf einige externe Resourcen zu, und es wäre wesentlich besser, wenn diese ihr stattdessen als Parameter übergeben würden.
    Ich meine konkret: Ex_Filter, Dateiliste, Suchbegriff, cbx_VersteckteDateien, CBx_Unterordner.

    Diese Dinge solltest du als Argumente der Methode übergeben.
    Dabei solltest du keine Controls übergeben, sondern nur die Werte der Controls.
    Also statt CBx_Unterordner reinzureichen, nur einen Boolean, nenne ihn mw. IncludeSubDirs

    Du könntest die Methode etwa als Shared modifizieren, dann sagt dir der Compiler sofort via Fehlermeldung, welche Variablen zu lokalisieren sind.

    Wie gesagt: reiche keine Controls herein, denn Controls vertragen sich überhaupt nicht mit Nebenläufigkeit.

    Humax schrieb:

    VB.NET-Quellcode

    1. Select Case CBx_VersteckteDateien.Checked
    2. Case "True"
    What :?:
    Gib Deinem Projekt schleunigst Option Strict On.
    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!

    RodFromGermany schrieb:


    What :?:
    Gib Deinem Projekt schleunigst Option Strict On.


    Komisch wieso ist das nicht strict on? Hatte das mal eingestellt. Könnte mir vorstellen, dass ich wohl bei Neuinstallation von VB2012 das nicht gesetzt habe. ?(
    Das würde ja heissen bei allen meinen neueren Projekten ist es nicht gesetzt!

    Humax schrieb:

    man muss es nur einmal einstellen
    Einmal für das aktuelle Projekt,
    einmal für das Studio als Default für alle dann neuen Projekte.
    Alle Projekte, die es nicht haben, musst Du händisch umstellen.
    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!