AGB  ·  Datenschutz  ·  Impressum  







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

TTask.WaitForAll hängt

Ein Thema von Smiley · begonnen am 24. Aug 2019 · letzter Beitrag vom 31. Okt 2022
Antwort Antwort
Seite 4 von 5   « Erste     234 5      
Schokohase
(Gast)

n/a Beiträge
 
#31

AW: TTask.WaitForAll hängt

  Alt 25. Aug 2019, 15:20
Ach ja, und wenn Du es umbaust, dass verwende eventuell ein TThread, nicht TTask..
Ein TThread hat ein 'OnTerminate' Event, welches bereits mit dem MainThread synchronisiert aufgerufen wird.
Das ist aber eine recht rückschrittliche Empfehlung. Es ist überhaupt kein Problem, in einem Task eine beliebige Aktion im Mainthread auszuführen - nicht nur beim Beenden:

Delphi-Quellcode:
  TTask.Run(
    procedure
    begin
      DirList;
      TThread.Queue(nil,
        procedure
        begin
          // tue was, nachdem DirList beendet wurde
        end;
    end);
Auch nicht dann, wenn der Main Thread mit WaitForAll auf die Ausführung wartet?
(Ernst gemeinte Frage, ich weiß das wirklich nicht, ich habe TTask noch nie verwendet.)
Nein, auch dann nicht ... und das hat NICHTS im Speziellen etwas mit TTask zu tun!

Jeder Task wird von einem Thread ausgeführt und darum gelten die gleichen Spielregeln wie bei einem Thread.

Man muss hier allerdings folgendes beachten:

TThread.Queue fügt den Code in eine Warteschlange, die erst dann abgearbeitet wird, wenn es zu einer Synchronisation mit dem MainThread kommt.
Wenn man also im MainThread auf einen oder mehrere Threads wartet, dann passiert bei TThread.Queue 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 machen wollte.

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.
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.453 Beiträge
 
Delphi 12 Athens
 
#32

AW: TTask.WaitForAll hängt

  Alt 25. Aug 2019, 16:09
Auch nicht dann, wenn der Main Thread mit WaitForAll auf die Ausführung wartet?
WaitForAll blockiert und blockieren ist nie gut. Dann kann ich den Thread-Code auch gleich im Hauptthread ausführen. Wenn ich solche sequentiellen Abläufe habe, bei denen man auf das Ende einer nebenläufigen Task warten muss, dann sollte man das anders implementieren. Eine Blaupause dafür gibt es aber leider nicht.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von dummzeuch
dummzeuch

Registriert seit: 11. Aug 2012
Ort: Essen
1.605 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#33

AW: TTask.WaitForAll hängt

  Alt 25. Aug 2019, 16:11
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!
Thomas Mueller
  Mit Zitat antworten Zitat
Schokohase
(Gast)

n/a Beiträge
 
#34

AW: TTask.WaitForAll hängt

  Alt 25. Aug 2019, 16:23
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!
Wenn du nur die Essenz von meinem langen Beitrag lesen möchtest, dann lese nur die Sätze mit den fetten Großbuchstaben und den roten Satz.

Es ist also ein Service.
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#35

AW: TTask.WaitForAll hängt

  Alt 25. Aug 2019, 17:05
Bitte sachlich und beim Thema bleiben.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
Benutzerbild von Smiley
Smiley

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

AW: TTask.WaitForAll hängt

  Alt 25. Aug 2019, 21:26
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.
  Mit Zitat antworten Zitat
Benutzerbild von Smiley
Smiley

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

AW: TTask.WaitForAll hängt

  Alt 25. Aug 2019, 21:55
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 ?
  Mit Zitat antworten Zitat
Rollo62

Registriert seit: 15. Mär 2007
4.094 Beiträge
 
Delphi 12 Athens
 
#38

AW: TTask.WaitForAll hängt

  Alt 26. Aug 2019, 08:31
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.
[/DELPHI]
Mich würde interessieren welche Ausnahmen da zum Beispiel in Frage kämen ?

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
  Mit Zitat antworten Zitat
Benutzerbild von Smiley
Smiley

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

AW: TTask.WaitForAll hängt

  Alt 27. Aug 2019, 18:20
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.
  Mit Zitat antworten Zitat
Benutzerbild von Smiley
Smiley

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

AW: TTask.WaitForAll hängt

  Alt 6. Sep 2019, 18:58
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.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 4 von 5   « Erste     234 5      


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 06:41 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