AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

DirList optimieren

Ein Thema von Smiley · begonnen am 28. Aug 2019 · letzter Beitrag vom 8. Sep 2019
Antwort Antwort
Benutzerbild von Smiley
Smiley

Registriert seit: 9. Dez 2004
Ort: Gedern
205 Beiträge
 
Delphi 10.4 Sydney
 
#1

DirList optimieren

  Alt 28. Aug 2019, 17:17
Nachdem mein letztes Thema "TTask.WaitForAll hängt" sich etwas vom Thema entfernt hat mache ich hier ein neues Thema auf.

Mein Ziel ist es Dateien auf meiner Festplatte oder im Netzwerk in einer Datenbak zu sammeln und dann zu sortieren, kategorisieren, packen und, mit Suchbegriffen leicht zu finden, in der DB abzulegen (Blob).

Das erste was zu tun war ist das einlesen der Dateien. Damit das effizient funktioniert, sollte das über threads abgewickelt werden.
Ein anderer Thread soll dann die Daten in eine Datenbank bringen.

Schokohase hat mir eine gute Grundlage für mein Projekt gegeben mit dem ich nun weiterarbeiten möchte.
Dafür nochmal Dank an Schokohase, der mir ein richtiges Beispiel zum ausprobieren gegeben hat.
Ich lerne mit Beispielen die funktionieren am besten.

Meine Version hat zwar auch funktioniert aber sie war schlecht programmiert und ohne eigene Klasse.

Noch habe ich aber nicht alles verstanden was er da programmiert hat und es funktioniert noch nicht ganz so wie ich es möchte.

Mir geht es nicht einfach darum ein Programm hinzubekommen, sondern darum Techniken und Teile von Delphi zu lernen die ich noch nicht kenne.

In diesem Projekt wird zum Beispiel, zur Anzeige, mit einer ListView gearbeitet, die ich bisher nie verwendet habe und nicht verstehe wie das Ereignis OnData getriggert wird und warum es dann die ganze Liste von FFiles einliest obwohl darin nur ein Datensatz zugewiesen wird.

Delphi-Quellcode:
procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem);
begin
  Item.Caption := FFiles[Item.Index];
  Item.SubItems.Add(TPath.GetDirectoryName(FFiles[Item.Index]));
  Item.SubItems.Add(TPath.GetFileName(FFiles[Item.Index]));
  Item.SubItems.Add('');
end;

procedure TForm1.PresentFiles(const Files: TStringDynArray;DirIndex:Integer);
 var
 i : integer;

begin
  FFiles.AddRange(Files);
  ListView1.Items.Count := FFiles.Count;
end;
Ich kann mir nur vorstellen dass das Zuweisen von FFiles.Count hier den Trigger auslöst.
Aber das ListView1Data bekommt doch nur einen Item übergeben und trotzdem ist die ganze Liste von FFiles im TreView enthalten.

Zweitens werden die Dateien im Ersten Verzeichnis , das ich durchsuche, nicht aufgelistet, nur der Ordner wir angezeigt. Beim ersten Durchlauf sind ja auch noch gar keine Files eingelesen worden.

Drittens möchte ich noch etwas mehr in die uDirlist Unit auslagern, damit das hier besser lesbar wird und ich möchte das Synchronize etwas minimieren.
Fehler und Infos können ja wenn möglich in der Klasse gespeichert werden und werden erst im nächsten PresentFiles ausgewertet.

Viertens: kann man die FFiles Liste nicht erst mal in einer Warteschlange speichern, beim übergeben (PresentFiles), damit das Einlesen der Daten gleich weitergehen kann und nicht auf die Ablage der Daten in ListView oder DB gewartet werden muss ?
Dann kann ein anderer Thread oder der Mainthread die Warteschlange dann nebenbei abarbeiten und die Daten irgendwo ablegen.

Ich hoffe die Community beteiligt sich hier mit konstruktiven Hinweisen, Danke.

Anbei bisher vorhandene Projekt DirList2.
Angehängte Dateien
Dateityp: zip Dirlist2.zip (54,4 KB, 10x aufgerufen)
  Mit Zitat antworten Zitat
Benutzerbild von p80286
p80286

Registriert seit: 28. Apr 2008
Ort: Stolberg (Rhl)
6.659 Beiträge
 
FreePascal / Lazarus
 
#2

AW: DirList optimieren

  Alt 29. Aug 2019, 09:36

Zweitens werden die Dateien im Ersten Verzeichnis , das ich durchsuche, nicht aufgelistet, nur der Ordner wir angezeigt. Beim ersten Durchlauf sind ja auch noch gar keine Files eingelesen worden.
Wenn nichts eingelesen wurde, gibt es nichts was angezeigt werden kann!?


Viertens: kann man die FFiles Liste nicht erst mal in einer Warteschlange speichern, beim übergeben (PresentFiles), damit das Einlesen der Daten gleich weitergehen kann und nicht auf die Ablage der Daten in ListView oder DB gewartet werden muss ?
Na klar geht das, aber... Threads sollten sich möglichst wenig Daten teilen. D.h. wenn ein Thread Dateinamen in eine Liste einliest, dann sollte die Oberfläche tunlichst die Finger von dieser Dateinamenliste lassen. Gleiches gilt für zwei oder mehr Threads die Dateinamen einlesen. Du solltest jedem eine eigene Dateinamenliste spendieren. Wenn das Einlesen eines Threads beendet ist, kannst Du dessen Dateinamenliste der Anzeige zur verfügung stellen. Oh natürlich ist mir Syncronise nich unbekannt. Wenn Du das allerdings zu oft anwendest, kannst Du Multithreading/Multitasking gleich vergessen.

Gruß
K-H
Programme gehorchen nicht Deinen Absichten sondern Deinen Anweisungen
R.E.D retired error detector
  Mit Zitat antworten Zitat
Benutzerbild von Smiley
Smiley

Registriert seit: 9. Dez 2004
Ort: Gedern
205 Beiträge
 
Delphi 10.4 Sydney
 
#3

AW: DirList optimieren

  Alt 29. Aug 2019, 15:05
Bei zweitens wollte ich damit sagen, dass ich zwar sehe, dass das Dateieinlesen erst nach Aufruf des PresentFiles kommt und damit die Einträge des Hauptordners nicht angezeigt werden können. Ich weiß nur nicht wie ich das ändern soll, wenn ich mich hinter die Dateileseroutine stelle, wird ja schon das nächste verzeichnis eingelesen.
Ich weiß nicht wie ich das am besten hinbekomme, habt ihr da irgendwelche Ideen ?


Bei viertens hatte ich mir gedacht eine Stringliste als Puffer zu nehmen die global ist und dort schreibe ich alle Strings aus PresentFiles rein mit Puffer.Add(files[i]).
Dann kann ich mit einen anderen Task die Puffer-Liste abarbeiten. Muss ich dabei mehr prüfen, als ob Puffer.count >0 denn es kann vorkommen, dass die Suchroutine in einem Verzeichnis sehr lang braucht um es aufzulisten (2,5 Minuten), in der Zeit könnte die Anzeigeroutine schon fertig sein. Hatte dabei auch schon an eine Ringpuffer Verwaltung gedacht, damit habe ich aber noch keine Erfahrung.
  Mit Zitat antworten Zitat
Benutzerbild von Smiley
Smiley

Registriert seit: 9. Dez 2004
Ort: Gedern
205 Beiträge
 
Delphi 10.4 Sydney
 
#4

AW: DirList optimieren

  Alt 30. Aug 2019, 11:43
Da ich nicht erkenne, wie ich das auflisten der Dateien im StartOrdner mit in die Klasse DirectoryUtils vernünftig einbaue, habe ich mir eine kleine Funktion gebastelt die mir die Dateien einliest und diese vor den Aufruf der DirectoryUtil Klasse gesetzt.

Delphi-Quellcode:
 StartFolder := 'C:\FB';
  FilterMaske := '*.*';

  StatusBar1.Panels[0].Text := 'suche Verzeichnisse';
  ListView1.Clear();
  ProgressBar1.Style := pbstMarquee;
  ProgressBar1.Position := 0;
  Button1.Enabled := False;

  //----------------------------------------------------
  PresentFiles(ListFiles(StartFolder,FilterMaske),0);
  //----------------------------------------------------

  FSearchDirTask := TDirectoryUtil.GetDirectoriesAsync(StartFolder, TSearchOption.soTopDirectoryOnly, nil,
    procedure(Directories: TStringDynArray; DirectoriesError: Exception)
    begin
      TThread.Synchronize(nil,
        procedure

Delphi-Quellcode:
function ListFiles(Path, FilterMaske: String): TStringDynArray;
Var
SearchResult:TStringDynArray;
Begin
  // Einlesen der Dateien ab dem Verzeichnis Path
  SearchResult := TDirectory.GetFiles(Path, FilterMaske, TSearchOption.soAllDirectories);
  Result:=SearchResult;
  end;
Wenn jemand eine Idee hat wie man das in der DirectoryUtil Klasse richtig macht, damit auch die Dateien im Hauptverzeichnis angezeigt werden, würde ich mich freuen.
  Mit Zitat antworten Zitat
Benutzerbild von Smiley
Smiley

Registriert seit: 9. Dez 2004
Ort: Gedern
205 Beiträge
 
Delphi 10.4 Sydney
 
#5

AW: DirList optimieren

  Alt 7. Sep 2019, 18:19
Ich habe jetzt nochmal von vorne begonnen mit so wenig wie möglich Komplexität.
Es wird pro Hauptverzeichnis ein Task geöffnet und die Ergebnisse über eine TaskQueue übergeben.

Dabei bekomme ich das Übergeben des jeweiligen Verzeichnisses an den neuen Task nicht hin.
Er zeigt immer nur das letzte Verzeichnis an.

Hier der Code:
Delphi-Quellcode:
unit uMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.StdCtrls,
  System.Types, System.Threading, System.Generics.Collections, System.ioUtils;

type
  TForm1 = class(TForm)
    btnStart: TButton;
    Grid: TStringGrid;
    Memo1: TMemo;
    lblZeitTeil: TLabel;
    lblZeitGesamt: TLabel;
    procedure btnStartClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;
    procedure PresentFiles(Files: TList<String>);

var
  Form1: TForm1;
  TaskCounter: Integer;
  lFiles: TList<String>;
  LTasks: TArray<ITask>;
  SuchMaske: String;
  MainVerz, FFiles: TStringDynArray;

implementation


{$R *.dfm}

//---------------------------------------------------------
procedure TForm1.btnStartClick(Sender: TObject);
var
  I, AktLine: Integer;
  StartFolder: String;

begin
  TaskCounter := 0;
  SetLength(LTasks, 10);
  lFiles.Clear;

  StartFolder := 'C:\FB';
  SuchMaske := '*.*';

  // Haupt Verzeichnisse einlesen
  MainVerz := TDirectory.GetDirectories(StartFolder, TSearchOption.soTopDirectoryOnly, nil);

  // dann die Dateien im Hauptverzeichnis auflisten
  FFiles := TDirectory.GetFiles(StartFolder, SuchMaske, TSearchOption.soTopDirectoryOnly, nil);
  lFiles.AddRange(FFiles);
  PresentFiles(lFiles);

  // dann alle Dateien in allen den Unterverzeichnissen auflisten
  TTask.Run(
    procedure
    var
      Verz: String;
      I: Integer;
    begin
      for I := 0 to High(MainVerz) do
        Begin
          Verz := MainVerz[i];
          LTasks[TaskCounter] := TTask.Run(
            procedure
            begin
              // Pro Verzeichnis alle Files auflisten
              TThread.Queue(nil,
                procedure
                begin
                  Memo1.Lines.Add('-----------------------------' + Verz);
                  lFiles.AddRange(TDirectory.GetFiles(Verz, SuchMaske, TSearchOption.soAllDirectories, nil));
                  PresentFiles(lFiles);
                end);

              System.MonitorEnter(self);
              try
                TaskCounter := TaskCounter + 1;
              finally
                System.MonitorExit(self);
              end;
            end);
        End;
    end);
end;
//---------------------------------------------------------

//---------------------------------------------------------
Procedure PresentFiles(Files: TList<String>);
var
  i: Integer;
begin
  for i := 0 to Files.Count - 1 do
    Begin
      Form1.memo1.Lines.Add(Files[i]);
    End;
  lFiles.Clear;
end;
//---------------------------------------------------------

//---------------------------------------------------------
procedure TForm1.FormCreate(Sender: TObject);
begin
  lFiles := TList<string>.Create();
end;
//---------------------------------------------------------

//---------------------------------------------------------
procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(lFiles);
end;
//---------------------------------------------------------

end.
anbei noch eine Lister der Ausgabe auf dem Bildschirm und der Inhalt des Verzeichnises.
Miniaturansicht angehängter Grafiken
dirlistausgabe.png   dirlistverzeichnis.png  
  Mit Zitat antworten Zitat
Benutzerbild von Smiley
Smiley

Registriert seit: 9. Dez 2004
Ort: Gedern
205 Beiträge
 
Delphi 10.4 Sydney
 
#6

AW: DirList optimieren

  Alt 8. Sep 2019, 16:15
Mittlerweile habe ich es hinbekommen meine Verzeichnisse über mehrere Tasks suchen und ausgeben zu lassen.
In PesentFiles() kann ich dann später meine Dateien in eine Datenbank schreiben und dort weiterverarbeiten.
Das wird dann der nächste Schritt, das gleichzeitige schreiben in eine DB.
Wobei die Tasks ja über Queue gesteuert sind und dadurch eigentlich nacheinander abgearbeitet weren sollten ???
Mal sehen ob das so funktioniert.

Zu dieser Version habe ich aber noch 3 Fragen:
1. Wie mache ich es am besten in der Predicate Function wenn ich zusätzlich noch 2 Strings ausfiltern will z.B. 'xy.tmp' und '*.xxx', ohne gleich mit Regex zu arbeiten, wie ich bei Sir Rufo gesehen habe, das ist mir zu unübersichtlich und kostet ja auch zusätzlich Zeit.

2. Das WaitForAll habe ich hier denke ich richtig eingesetzt und trotzdem kommt immer die 'Array ist nil' Meldung. Ist zwar nicht wichtig, wollte aber mal wissen was hier falsch läuft.

3. Das ganze in eine Klasse einbauen, an die man nur noch das Suchverzeichnis und die include/exclude Maske übergibt. PresentFiles als Procedure, die man selbst dann anpassen kann.

Hier ein Stück Code für Problem 1 und 2

Delphi-Quellcode:
 Predicate := function(const Path: string; const SearchRec: TSearchRec): Boolean
               begin
                Result := (SearchRec.Attr and faHidden)=0;
               end;

  // Haupt Verzeichnisse einlesen
  MainVerz := TDirectory.GetDirectories(StartFolder, TSearchOption.soTopDirectoryOnly, nil);

  // Task Anzahl auf Anzahl der verzeichnisse setzen
  SetLength(LTasks, High(MainVerz)+1);

  // dann die Dateien im Hauptverzeichnis auflisten
  FFiles := TDirectory.GetFiles(StartFolder, SuchMaske, TSearchOption.soTopDirectoryOnly, Predicate);
  If High(FFiles)>0 Then PresentFiles(FFiles);
  ProgressBar1.Max := High(MainVerz);

  // dann alle Dateien in allen Unterverzeichnissen auflisten
  for I := 0 to High(MainVerz) do
    Begin
      Verz := MainVerz[I];
      LTasks[TaskCounter] := Form1.ListDir(Verz);
      Application.ProcessMessages;
      ProgressBar1.Position := TaskCounter;
      Application.ProcessMessages;
    End;

  // Schleife zuende dann warten bis alle Threads Beendet sind um die Zeit zu messen
  //TTask.WaitForAll(LTasks); // <---- Meldet immer 'Mindestens ein Array ist nil'
  lblZeitGesamt.Caption := 'Gesamt Zeit: ' + FormatDateTime('hh:nn:ss:zzz', Now - FirstTime);
 end;
Das gesamte Projekt ist im Anhang.

Verbesserungs Vorschläge sind immer gern gesehen.
Angehängte Dateien
Dateityp: zip DirListTaskV1.zip (54,5 KB, 6x aufgerufen)
  Mit Zitat antworten Zitat
Benutzerbild von Smiley
Smiley

Registriert seit: 9. Dez 2004
Ort: Gedern
205 Beiträge
 
Delphi 10.4 Sydney
 
#7

AW: DirList optimieren

  Alt 8. Sep 2019, 16:36
Im Kursus von Olaf Monien habe ich gesehen, dass die TTask Klasse sich selbst um die Auslastung der CPU kümmert und nur soviel Tasks (threads) aufmacht bis die Auslastung des Systems bei 95 Prozent ist, vorher werden keine weiteren Tasks aufgemacht.
Das funktioniert bei mir nicht oder ich habe zu wenig Last.
Ich muss mit SetLength(LTasks, High(MainVerz)+1) immer angeben wieviele Tasks ich benötige. Ich hatte gehofft die Task Klasse kümmert sich selbst um die Anzahl an Tasks und löscht die abgeschlossenen dann auch wieder, sodass nicht zuviele gleichzeitig laufen.
Würde das gerne begrenzen, damit nur 10 gleichzeitige Tasks laufen, da die Festplatte sich mit mehr Tasks auch nicht schneller ansprechen lässt.
Lediglich beim wegschreiben bringt es etwas, aber da ich die Tasks über eine Queue laufen lasse (wegen dem wegschreiben in DB) dürfte das ja dann auch nicht mehr bringen.

Nach dem ich mir den Kursus angesehen hatte, dachte ich, dass ich alles verstanden habe, aber in der Praxis sieht es dann immer mal wieder alles anders aus.

Frage:
Werden die Tasks die ich in die Queue schreibe wirklich nacheinander abgearbeitet und ich kann bedenkenlos im PresentFiles() in meine Datenbank schreiben ?
Kann ich die Anzahl der gleichzeitig laufenden Tasks begrenzen ?
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:56 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz