Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Rekursion in Thread


J.Borchert - Do 17.05.12 08:39
Titel: Rekursion in Thread
Leider klappt mein Versuch der Rekursion innerhalb eines Threads nicht.

Ist das jetzt nur wegen des "Execute"-Aufruf, sollte ich so ziemlich alles was in Execute stattfindet in eine separate Funktion auslagern, die sich dann selbst aufrufen kann?

Und btw: Proceduren die in Synchronise() aufgerufen werden, können die auch Parameter haben?

Einen dennoch schönen Herrentag wünscht Jürgen


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
unit FFThread;

interface

uses
  Classes, SysUtils, ComCtrls;

type
  pNodeInfo = ^tNodeInfo;
  tNodeInfo = RECORD
    Path: boolean;
    Name: string;
  END;

type
  FindFileThread = class(TThread)
  private
    { Private-Deklarationen }
    PROCEDURE SetzeAnode;
    Procedure SetzeAnodeTMP;
  protected
    procedure Execute; override;
  end;

VAR SearchRec: tSearchRec;  NodeTmp: tTreeNode;  Info: pNodeInfo;
    i: integer;  MaskeOK : Boolean;  s : string;

implementation
uses Unit1, jobs1;

{ FindFileThread }

procedure setAnode(const i:integer;const aN:tTreeNode);
  begin aN.ImageIndex := i;
        aN.SelectedIndex := i;
        aN.StateIndex := i;
  end;

procedure FindFileThread.SetzeAnode;
begin aNode := aTree.Items.AddChildObject(aNode, SearchRec.Name, Info);
      setAnode(5, aNode); // Grund-Icon (Ordner)
end;
procedure FindFileThread.SetzeAnodeTMP;
begin NodeTMp := aTree.Items.AddChildObject(aNode, SearchRec.Name, Info);
      setAnode(9, nodeTMP);
          if POS('DOC', s) > 0 then setAnode(6, nodeTMP);
          if POS('FIX', s) > 0 then setAnode(7, nodeTMP);
          if POS('XLS', s) > 0 then setAnode(16, nodeTMP);
          if POS('CSV', s) > 0 then setAnode(3, nodeTMP);
          if POS('VER', s) > 0 then setAnode(13, nodeTMP);
          if POS('GEO', s) > 0 then setAnode(1, nodeTMP);
          if POS('HT', s) > 0 then setAnode(10, nodeTMP);
          if POS('TXT', s) > 0 then setAnode(8, nodeTMP);
          if POS('PDF', s) > 0 then setAnode(11, nodeTMP);
          if POS('JPG', s) > 0 then setAnode(12, nodeTMP);
          if POS('BMP', s) > 0 then setAnode(2, nodeTMP);
          if POS('GIF', s) > 0 then setAnode(12, nodeTMP);
          if POS('MPG', s) > 0 then setAnode(15, nodeTMP);
          if POS('AVI', s) > 0 then setAnode(15, nodeTMP);
          if POS('TIF', s) > 0 then setAnode(12, nodeTMP);
          if POS('ZIP', s) > 0 then setAnode(0, nodeTMP);
          if POS('RTF', s) > 0 then setAnode(15, nodeTMP);
end;

procedure FindFileThread.Execute;
BEGIN aTree.Items.BeginUpdate;
   // Wenn am Ende der Pfadangabe noch kein \ steht, dieses hinzufügen
  IF aPath[Length(aPath)] <> '\' THEN aPath := aPath + '\';
  IF FindFirst(aPath + '*.*', faDirectory, SearchRec) = 0 THEN
   BEGIN
    REPEAT
        // Wenn es sich um ein Verzeichnis handelt
      IF (SearchRec.Attr AND faDirectory = faDirectory) AND
        (SearchRec.Name[1] <> '.'AND
        (SearchRec.Name <> '_Privat'AND
        (SearchRec.Name <> '_Temp'AND
        (SearchRec.Name <> '_Help'AND
        (SearchRec.Name <> '_Config'THEN
       BEGIN
        IF (SearchRec.Attr AND faDirectory > 0THEN
         BEGIN
          New(Info);
          Info^.Path := true;
          Info^.Name := aPath + SearchRec.Name;
                // zum aktuellen Eintrag hinzufügen
          Synchronize(SetzeAnode);
         END;
            // Eintrag merken
        NodeTmp := aNode.Parent;
            // auf Untereinträge prüfen
        aPath := aPath + SearchRec.Name;
        Execute;
            // Eintrag wiederholen
        aNode := NodeTmp;
       END
      ELSE // Eintrag ist eine Datei
       Begin for i:=1 to AnzStr(' ',Maske) do
              if POS( strx(' ',Maske,i), uppercase(ExtractFileExt(SearchRec.Name))) > 0
               then begin MaskeOK:=True; break; end else MaskeOK:=False;
        if FileIsInDelList(SearchRec.Name)then MaskeOK:=False;// Liste der zu löschenden
        IF aWithFiles AND (SearchRec.Name <> '.'AND
          (SearchRec.Name <> '..'AND
          ( (MaskeOK=True) or
            (Maske='')
          )
        THEN
         BEGIN
          New(Info);
          Info^.Path := false;
          Info^.Name := aPath + SearchRec.Name;
          s := uppercase(ExtractFileExt(SearchRec.Name));
          Synchronize(SetzeAnodeTMP);
         END;
       end;
      // solange weiter bis keine wieteren Dateien/Verzeichniss gefunden werden
    UNTIL FindNext(SearchRec) <> 0;
    FindClose(SearchRec);
   END;
  aTree.Items.EndUpdate;
END;

end.


jaenicke - Do 17.05.12 09:55

user profile iconJ.Borchert hat folgendes geschrieben Zum zitierten Posting springen:
Ist das jetzt nur wegen des "Execute"-Aufruf, sollte ich so ziemlich alles was in Execute stattfindet in eine separate Funktion auslagern, die sich dann selbst aufrufen kann?
Das ist so oder so sinnvoll um die Funktionalität besser zu kapseln. Im Moment ist das ganze durch die globalen Variablen usw. extrem unübersichtlich und fehleranfällig.

BeginUpdate und EndUpdate hast du vergessen synchronisiert aufzurufen.

user profile iconJ.Borchert hat folgendes geschrieben Zum zitierten Posting springen:
Und btw: Proceduren die in Synchronise() aufgerufen werden, können die auch Parameter haben?
Nein, das wird normalerweise über ein privates Feld geregelt. Ein Beispiel, auch hinsichtlich der Anmerkungen unten:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
type
  TOnDoSomething = procedure(const AParam: stringof object;

  TMyThread = class(TThread)
  private
    FMyParam: string;
    FOnDoSomething: TOnDoSomething;
    FTree: TTreeView;
    procedure MyMethod;
  public
    constructor Create(ATree: TTreeView; AOnDoSomething: TOnDoSomething);
    procedure Execute; override;
  end;

// ...

constructor Create(ATree: TTreeView; AOnDoSomething: TOnDoSomething);
begin
  inherited Create(False);
  FTree := ATree;
  FOnDoSomething := AOnDoSomething;
end;

procedure TMyThread.MyMethod;
begin
  if Assigned(FOnDoSomething) then
    FOnDoSomething(FMyParam);
end;

procedure TMyThread.Execute;
var
  i: Integer; // lokal deklariert reicht
begin
  FMyParam := 'Example';
  Synchronize(MyMethod); // MyMethod ruft die eigentliche Methode mit dem gespeicherten Parameter auf

  FTree. ... // FTree wurde im Konstruktor übergeben und steht daher bereit
end;
Ein paar Hinweise:

Bei neueren Delphiversionen gibt es noch bessere Möglichkeiten, die die Synchronisation verbessern und vereinfachen, aber ich schätze mal die Delphiversion 7 im Profil stimmt noch, oder? Deshalb lasse ich das mal alles außen vor.


J.Borchert - Do 17.05.12 18:38

Danke für die ausführliche Hilfe.

Das mit dem .BeginUpdate usw. habe ich bereits bemerkt. Für mich war interessant, daß mein Thread trotzdem erstmal arbeitete, aber immer wenn er rekursiv (bei einem Verzeichnis) sich aufrufen wollte, brach er einfach komplett ab.
Das mit den Variablen ist wirklich blöd, daran werde ich erst mal arbeiten. Auch deine Hinweise (T.. E..) werde ich mir zu Herzen nehmen.

Danke nochmals für die Hilfe, vor allem für das Beispiel. Ich glaube so komme ich erst mal weiter.

Schönen TRestHerrenTag ohne EUeberGeben ;-) wünscht Jürgen