![]() |
DirList optimieren
Liste der Anhänge anzeigen (Anzahl: 1)
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:
Ich kann mir nur vorstellen dass das Zuweisen von FFiles.Count hier den Trigger auslöst.
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; 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. |
AW: DirList optimieren
Zitat:
Zitat:
Gruß K-H |
AW: DirList optimieren
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. |
AW: DirList optimieren
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:
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.
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; |
AW: DirList optimieren
Liste der Anhänge anzeigen (Anzahl: 2)
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:
anbei noch eine Lister der Ausgabe auf dem Bildschirm und der Inhalt des Verzeichnises.
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. |
AW: DirList optimieren
Liste der Anhänge anzeigen (Anzahl: 1)
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:
Das gesamte Projekt ist im Anhang.
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; Verbesserungs Vorschläge sind immer gern gesehen. |
AW: DirList optimieren
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 ? |
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:31 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz