![]() |
AW: TTask.WaitForAll hängt
Zitat:
Delphi-Quellcode:
zu tun!
TTask
Jeder Task wird von einem Thread ausgeführt und darum gelten die gleichen Spielregeln wie bei einem Thread. Man muss hier allerdings folgendes beachten:
Delphi-Quellcode:
fügt den Code in eine Warteschlange, die erst dann abgearbeitet wird, wenn es zu einer Synchronisation mit dem MainThread kommt.
TThread.Queue
Wenn man also im MainThread auf einen oder mehrere Threads wartet, dann passiert bei
Delphi-Quellcode:
erstmal gar nichts. Erst wenn der/die Thread/s beendet sind (auf den/die da gewartet wurde) dann erfolgt das was man per
TThread.Queue
Delphi-Quellcode:
machen wollte.
TThread.Queue
Am besten ist es wenn man im MainThread NIEMALS auf einen Thread wartet. Die Ausnahmen von dieser Regel sollten sehr spärlich sein, sonst drohen eben DeadLocks oder seltsame Verhalten. Und das es anders geht zeige ich mal mit einem kleinen Beispiel.
Delphi-Quellcode:
unit AsyncWithTasks.MainForm;
interface uses Winapi.Windows, Winapi.Messages, System.Generics.Collections, System.SysUtils, System.Variants, System.Classes, System.Threading, System.Types, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.ComCtrls, Vcl.StdCtrls; type TForm1 = class(TForm) Button1: TButton; ProgressBar1: TProgressBar; Label1: TLabel; ListView1: TListView; Panel1: TPanel; procedure FormDestroy(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure ListView1Data(Sender: TObject; Item: TListItem); private { Private-Deklarationen } FSearchDirTask: ITask; FFiles: TList<string>; procedure PresentFiles(const Files: TStringDynArray); public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} uses System.IOUtils; type TDirectoryUtil = record public class function GetDirectoriesAsync(const Path: string; const SearchOption: TSearchOption; const Predicate: TDirectory.TFilterPredicate; const Completion: TProc<TStringDynArray, Exception>): ITask; static; class function GetFilesAsync(const Path, SearchPattern: string; const SearchOption: TSearchOption; const Completion: TProc<TStringDynArray, Exception>) : ITask; static; end; procedure TForm1.FormDestroy(Sender: TObject); begin if Assigned(FSearchDirTask) then FSearchDirTask.Cancel(); FreeAndNil(FFiles); end; procedure TForm1.FormCreate(Sender: TObject); begin FFiles := TList<string>.Create(); end; procedure TForm1.Button1Click(Sender: TObject); var StartFolder, FilterMaske: string; begin StartFolder := 'Z:\Dokumente'; FilterMaske := '*.*'; Label1.Caption := 'suche Verzeichnisse'; ListView1.Clear(); ProgressBar1.Style := pbstMarquee; ProgressBar1.Position := 0; Button1.Enabled := False; FSearchDirTask := TDirectoryUtil.GetDirectoriesAsync(StartFolder, TSearchOption.soTopDirectoryOnly, nil, procedure(Directories: TStringDynArray; DirectoriesError: Exception) begin TThread.Synchronize(nil, procedure var dirIndex: Integer; filesCompletion: TProc<TStringDynArray, Exception>; begin if Assigned(DirectoriesError) then begin Label1.Caption := DirectoriesError.ToString(); Button1.Enabled := True; end else begin ProgressBar1.Max := High(Directories) + 1; ProgressBar1.Style := pbstNormal; dirIndex := 0; filesCompletion := procedure(Files: TStringDynArray; FilesError: Exception) begin TTask.CurrentTask.CheckCanceled(); TThread.Synchronize(nil, procedure begin if Assigned(FilesError) then begin end else begin PresentFiles(Files); end; Inc(dirIndex); ProgressBar1.Position := dirIndex; if dirIndex <= High(Directories) then begin Label1.Caption := 'search in ' + Directories[dirIndex]; FSearchDirTask := TDirectoryUtil.GetFilesAsync(Directories[dirIndex], FilterMaske, TSearchOption.soAllDirectories, filesCompletion); end else begin Label1.Caption := 'completed'; Button1.Enabled := True; FSearchDirTask := nil; end; end); end; Label1.Caption := 'search in ' + Directories[dirIndex]; FSearchDirTask := TDirectoryUtil.GetFilesAsync(Directories[dirIndex], FilterMaske, TSearchOption.soAllDirectories, filesCompletion); end; end); end); end; 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); begin FFiles.AddRange(Files); ListView1.Items.Count := FFiles.Count; end; { TDirectoryUtil } class function TDirectoryUtil.GetDirectoriesAsync(const Path: string; const SearchOption: TSearchOption; const Predicate: TDirectory.TFilterPredicate; const Completion: TProc<TStringDynArray, Exception>): ITask; begin if not Assigned(Completion) then raise EArgumentNilException.Create('Completion'); Result := TTask.Run( procedure var res: TStringDynArray; begin res := default (TStringDynArray); try res := TDirectory.GetDirectories(Path, SearchOption, Predicate); Completion(res, nil); except on E: Exception do begin Completion(res, E); raise; end; end; end); end; class function TDirectoryUtil.GetFilesAsync(const Path, SearchPattern: string; const SearchOption: TSearchOption; const Completion: TProc<TStringDynArray, Exception>): ITask; begin if not Assigned(Completion) then raise EArgumentNilException.Create('Completion'); Result := TTask.Run( procedure var res: TStringDynArray; begin res := default (TStringDynArray); try res := TDirectory.GetFiles(Path, SearchPattern, SearchOption); Completion(res, nil); except on E: Exception do begin Completion(res, E); raise; end end; end); end; end. |
AW: TTask.WaitForAll hängt
Zitat:
|
AW: TTask.WaitForAll hängt
Danke für die Erklärung Schokhase.
Aber muss das unbedingt in GROSSBUCHSTABEN und rot sein? Ich habe dann immer das Gefühl, Du willst allen zeigen, wie dumm ich doch bin. Das wissen die aber doch alle schon! |
AW: TTask.WaitForAll hängt
Zitat:
Es ist also ein Service. |
AW: TTask.WaitForAll hängt
Bitte sachlich und beim Thema bleiben.
|
AW: TTask.WaitForAll hängt
Ich hatte heute Morgen meinen Code noch mit Synchronize abgesichert und es lief auch ganz gut, dann musste ich weg.
Jetzt schaue ich hier ins Forum und sehe das Beispiel von Schokohase (unit AsyncWithTasks.MainForm). Die einzelnen Verzeichnisse in Striglisten zu speichern und dann an den MainThread weiterzugeben hatte ich für später auch schon geplant, wollte aber erst mal klein anfangen, was, wie man sieht, nicht sinnvoll ist. Im Schokohasen Beispiel ist ja schon ziemlich alles drin was ich insgesamt erreichen wollte. Höchstens nochmal versuchen ob Pooling (task.Queue) noch was bringt. Wenn ich das richtig verstehe, wird ein FSearchDirTask := TDirectoryUtil.GetDirectoriesAsync aufgemacht der dann rekursiv durch die Verzeichnisse geht und pro Verzeichnist dann eine Stringliste an den Mainthread übergibt (PresentFiles(Files)). Das "filesCompletion: TProc<TStringDynArray, Exception>;" muss ich mir noch genauer ansehen um das errorhandling zu verstehen und wie ich das verarbeite. Ist "FFiles: TList<string>;" eine TStringlist ? Diese Schreibweise kenne ich noch nicht. Dann fange ich am besten nochmal von vorne an, indem ich den SchokohasenThread als Vorlage benutze. Danke Schokohase für den kompletten benutzbaren Code. Ein Beispiel sagt mir mehr als viele Diskussionen. Melde mich wieder, wenn ich es umgesetzt habe. |
AW: TTask.WaitForAll hängt
Das "FFiles: TList<string>;" habe ich mir mal nachgeschlagen, ist eine TList Klasse, damit habe ich bisher nicht gearbeitet, scheint sehr nützlich zu sein.
Ist das von der Geschwindigkeit nur unbedeutend langsamer als eine Striglist oder DynArray ? Wenn ich es richtig verstehe wird mit jedem Verzeichnis die TList erweitert und enthält am Ende alle Files mit Verzeichnisname ? In "PresentFiles" wird dem Treview immer wieder die,zu diesem Zeitpunkt, gesamte Liste zugewiesen, ist das richtig ? |
AW: TTask.WaitForAll hängt
Zitat:
Womöglich sowas wie Aufteilung einer Aufgabe in mehrere Threads, und dann warten bis alle Fertig sind. Ich würde aber auch dafür eigentlich nicht im MainUI darauf warten wollen. Oder hast Du dafür ein besonderes Beispiel, wo man im UT Thread warten MUSS ? P.S. rot und fett finde ich gut. Weiter so, damit der Blick beim Wesentlichen bleibt :thumb: |
AW: TTask.WaitForAll hängt
Ich habe jetzt mal eine Schoko-Version erstellt und angepasst nach der Vorgabe von Schokohase.
Das ganze funktioniert grob ganz gut mit der Ausnahme, dass die Dateien im ersten (ausgewählten) Verzeichnis nicht angezeigt werden. Weiterhin wird im ListView nur der komplette Dateiname mit Pfad angezeigt und nicht einzeln, wie die ListView1Data Routine vermuten lässt. Außerdem verstehe ich noch nicht was die ListView1Data Triggert und warum sie nicht nur, einen Eintrag, sondern alle aus FFiles in die ListView schreibt. Um die Werte auch mal in eine TreeView anzeigen zu können müsste ich doch auch den Subdirectory Level haben, habe aber nur den HauptLevel in DirIndex, wie lässt sich das machen ? Hier mal mein Code. Habe zwei Units daraus gemacht und möchte noch einiges mehr in die uDirlist auslagern.
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.ComCtrls, Vcl.StdCtrls, System.Generics.Collections, System.Types, System.Threading, System.IOUtils; type TForm1 = class(TForm) TreeView1: TTreeView; Button1: TButton; ProgressBar1: TProgressBar; StatusBar1: TStatusBar; ListView1: TListView; ListBox1: TListBox; ListBox2: TListBox; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); procedure PresentFiles(const Files: TStringDynArray;DirIndex:Integer); procedure ListView1Data(Sender: TObject; Item: TListItem); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; FSearchDirTask: ITask; FFiles: TList<string>; implementation {$R *.dfm} uses uDirlist; procedure TForm1.FormCreate(Sender: TObject); begin FFiles := TList<string>.Create(); ListView1.OwnerData := True; ListView1.ViewStyle:=vsList; end; procedure TForm1.FormDestroy(Sender: TObject); begin if Assigned(FSearchDirTask) then FSearchDirTask.Cancel(); FreeAndNil(FFiles); end; procedure TForm1.Button1Click(Sender: TObject); var StartFolder, FilterMaske: string; begin StartFolder := 'C:\xampp'; FilterMaske := '*.*'; StatusBar1.Panels[0].Text := 'suche Verzeichnisse'; ListView1.Clear(); ProgressBar1.Style := pbstMarquee; ProgressBar1.Position := 0; Button1.Enabled := False; FSearchDirTask := TDirectoryUtil.GetDirectoriesAsync(StartFolder, TSearchOption.soTopDirectoryOnly, nil, procedure(Directories: TStringDynArray; DirectoriesError: Exception) begin TThread.Synchronize(nil, procedure var dirIndex: Integer; filesCompletion: TProc<TStringDynArray, Exception>; begin if Assigned(DirectoriesError) then begin StatusBar1.Panels[0].Text := DirectoriesError.ToString(); Button1.Enabled := True; end else begin ProgressBar1.Max := High(Directories) + 1; ProgressBar1.Style := pbstNormal; dirIndex := 0; filesCompletion := procedure(Files: TStringDynArray; FilesError: Exception) begin TTask.CurrentTask.CheckCanceled(); TThread.Synchronize(nil, procedure begin if Assigned(FilesError) then begin StatusBar1.Panels[1].Text:='Einlesefehler: ' end else begin PresentFiles(Files,DirIndex); end; Inc(dirIndex); ProgressBar1.Position := dirIndex; if dirIndex <= High(Directories) then begin StatusBar1.Panels[0].Text := 'search in ' + Directories[dirIndex]; FSearchDirTask := TDirectoryUtil.GetFilesAsync(Directories[dirIndex], FilterMaske, TSearchOption.soAllDirectories, filesCompletion); end else begin StatusBar1.Panels[0].Text := 'completed'; Button1.Enabled := True; FSearchDirTask := nil; end; end); end; StatusBar1.Panels[0].Text := 'search in ' + Directories[dirIndex]; FSearchDirTask := TDirectoryUtil.GetFilesAsync(Directories[dirIndex], FilterMaske, TSearchOption.soAllDirectories, filesCompletion); end; end); end); end; 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; end.
Delphi-Quellcode:
unit uDirlist;
interface uses System.Generics.Collections, System.SysUtils, System.Variants, System.Classes, System.Threading, System.Types, Vcl.Graphics, Vcl.Controls, Vcl.StdCtrls, System.IOUtils,uMain; type TDirectoryUtil = record public class function GetDirectoriesAsync(const Path: string; const SearchOption: TSearchOption; const Predicate: TDirectory.TFilterPredicate;const Completion: TProc<TStringDynArray, Exception>): ITask; static; class function GetFilesAsync(const Path, SearchPattern: string; const SearchOption: TSearchOption; const Completion: TProc<TStringDynArray, Exception>): ITask; static; end; implementation { TDirectoryUtil } class function TDirectoryUtil.GetDirectoriesAsync(const Path: string; const SearchOption: TSearchOption; const Predicate: TDirectory.TFilterPredicate; const Completion: TProc<TStringDynArray, Exception>): ITask; begin if not Assigned(Completion) then raise EArgumentNilException.Create('Completion'); Result := TTask.Run( procedure var res: TStringDynArray; begin res := default (TStringDynArray); try res := TDirectory.GetDirectories(Path, SearchOption, Predicate); Completion(res, nil); except on E: Exception do begin Completion(res, E); raise; end; end; end); end; class function TDirectoryUtil.GetFilesAsync(const Path, SearchPattern: string; const SearchOption: TSearchOption; const Completion: TProc<TStringDynArray, Exception>): ITask; begin if not Assigned(Completion) then raise EArgumentNilException.Create('Completion'); Result := TTask.Run( procedure var res: TStringDynArray; begin res := default (TStringDynArray); try res := TDirectory.GetFiles(Path, SearchPattern, SearchOption); Completion(res, nil); except on E: Exception do begin Completion(res, E); raise; end end; end); end; end. |
AW: TTask.WaitForAll hängt
Die Antwort auf meine Frage TTask.WaitForAll wird in dem Kurs von Olaf Monien sehr gut erklärt:
Delphi CE Bootcamp 2018 Week 7 - Threading and Performance Tuning with Olaf Monien Part 3 etwa bei 40 bis 47 Minuten dort wird auch der Sinn dieses Befehls erklärt, in bestimmten Situationen. Das war die Antwort die ich finden wollte. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:55 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