Autor Beitrag
seji
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Do 15.10.09 20:20 
Hallo zusammen,

ich bins schon wieder ^^

Wie oben schon genannt habe ich ein problem mit Threads.
Ich weiß dazu gibts hier ne Menge im Forum aber nix hat so wirklich mein Problem beschrieben.

Also, was ich im dem Thread machen möchte sind Dateien kopieren, desweiteren will ich das im ne Thread machen weil man dann ja auch den kopiervorgang
abbrechen kann. Dazu habe ich eine Hauptklasse mit GUI(Unit1) etc. und eine mit dem Thread (Unit2).

Den Thread ruf ich in der Hauptklasse wie folgt auf:

ausblenden volle Höhe 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:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, StdCtrls, filectrl,Unit2,
  Menus;

type
.
.
.

 private
    { Private declarations }
    
     copyGUI:copy;
     
  public
    { Public declarations }
  end;

var
  .
  .
  .

begin

   .
   .
   .

copyGUI.create;
copyGUI.Setpathm3u('C:\Users\All Users\Temp\parsed_playlist.txt');
copyGUI.Setpathcopyto(pathcopyto);

copyGUI.Resume;

 .
 .
 .
end;

procedure TForm1.cancelClick(Sender: TObject);
begin
copyGUI.Terminate;

end;


In meiner Thread Unit steht dann folgendes:


ausblenden volle Höhe 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:
123:
124:
unit Unit2;


interface

uses
  classes,
  sysUtils,
  windows;

type
   copy = CLASS(TThread)
   

     private
       pathm3u : String;
       pathcopyto : String;


     protected
        procedure execute; override;
     public
        constructor create; virtual;
        procedure Setpathm3u (ppathm3u: String); virtual;
        procedure Setpathcopyto (ppathcopyto: String); virtual;

   end;

implementation

uses Unit1;


//-------- execute (protected) -----------------------------------------
procedure copy.execute;

var
title:String;
filename:String;
readm3u:String;
extension:String;
InFile : TextFile;
Position:Integer;



begin
while not Terminated do;


error_notfound:=0;
assignFile (InFile, pathm3u);
reset (InFile);


Form1.Memo1.Lines.Add('['+TimeToStr(Time)+'] '+'Exportiere Datein, bitte warten...');

while not eof (InFile) do
  begin
  readln (InFile, readm3u);
  Position := Pos(':', readm3u);
  //Filename zusammenbasteln
  if Position <> 0 then begin
    extension:=ExtractFileExt(readm3u);
    filename:=pathcopyto+title+extension;

   //Kopiervorgang

      if Form1.CheckBox1.Checked=True then begin
        if not FileExists(PChar(readm3u)) then begin
          Form1.Memo1.Lines.Add('Die Datei '+PChar(readm3u)+' konnte nich gefunden werden');
          error_notfound:=error_notfound+1;
        end
        else begin
          CopyFile( PChar(readm3u) ,PChar(filename),false);
        end;
      end
      else begin
        if not FileExists(PChar(readm3u)) then begin
          Form1.Memo1.Lines.Add('Die Datei '+PChar(readm3u)+' konnte nich gefunden werden');
          error_notfound:=error_notfound+1;
        end
        else begin
          CopyFile( PChar(readm3u) ,PChar(filename),true);
        end;
   end;

  // Progressbar Balken
  count:=Round(count_temp);
  Form1.ProgressBar1.Max:=count;
  Form1.Progressbar1.Position := Form1.Progressbar1.Position + 1;

  end
  else begin
  title:=readm3u;

  end;

  end;//while end
CloseFile (Infile);

end;

//-------- create (public) ---------------------------------------------
constructor copy.create;
begin
  inherited create(true); // CreateSuspended = true
  freeOnTerminate := true;
end;

//-------- Setpathm3u (public) ----------------------------------------------
procedure copy.Setpathm3u (ppathm3u: String);
begin
  pathm3u := ppathm3u;
end;

//-------- Setpathcopyto (public) ----------------------------------------------
procedure copy.Setpathcopyto (ppathcopyto: String);
begin
  pathcopyto := ppathcopyto;
end;


end.


beim Compelieren komme keinerlei Fehler. Ich hab das ganze nach nem Tutorial gemacht.

Nur leider kommt sobald den Thread aufrufe/erstelle folgender Fehler:

error01

Bei folgender zeile stiegt mein programm aus:

ausblenden Delphi-Quelltext
1:
copyGUI.Setpathm3u('C:\Users\All Users\Temp\parsed_playlist.txt');					



Muss dahher irgendwas im Thread aber ich hab keinen Plan was. Des weiteren wundert mich es das es schon beim create passiert
und nicht erst wenn der Thread überhaupt mal was macht.

Meine vermutung ist das ich was mit syncronize() machen muss weil ich ja auf Ubit1 zugreife. Hab ich dann auch probiert indem ich aus Unit2 alles rausgenommen habe was mit Unit1 zu tun hat. Bekomme ich aber den selben Fehler.

Vielen Dank schonmal für eure Hilfe.

Gruß Seji


PS: Habe mich an folgendes Tutorial gehalten: www.hsg-kl.de/faeche...hi/threads/index.php


Moderiert von user profile iconNarses: Topic aus Sonstiges (Delphi) verschoben am Do 15.10.2009 um 20:24
Einloggen, um Attachments anzusehen!
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19321
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 15.10.09 20:24 
user profile iconseji hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
copyGUI.create;
copyGUI.Setpathm3u('C:\Users\All Users\Temp\parsed_playlist.txt');
copyGUI.Setpathcopyto(pathcopyto);
[...]
Du rufst die Methode Create des noch nicht existierenden Objekts copyGUI auf, wie soll das gehen? :gruebel:

// EDIT:
Im Tutorial steht es ja auch wie nicht anders zu erwarten richtig, aber wenn du nicht abschreiben kannst... ;-)
seji Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Do 15.10.09 21:10 
Oh stimmt das ist ja mega peinlich ....
Da hab ich wohl den Baum vor lauter bäumen net gesehen.

Also dafür schonmal vielen Dank!!

Hätte da aber noch ne frage wegen Terminate. Ich hab nochmal im tut nachgeschaut, so wie ich das sehe hab ich da aber keinen fehler gemacht.
Also mein programm kopiert fleißig aber wenn ich auf cancel drücke passiert leider nix. Da ich meine Thread Unit noch etwas verändert habe, poste ich sie hier nochmal:

ausblenden volle Höhe 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:
123:
124:
125:
126:
127:
unit Unit2;


interface

uses
  classes,
  sysUtils,
  windows;

type
   copy = CLASS(TThread)
   

     private
       pathm3u : String;
       pathcopyto : String;


     protected
        procedure execute; override;
     public
        constructor create; virtual;
        procedure Setpathm3u (ppathm3u: String); virtual;
        procedure Setpathcopyto (ppathcopyto: String); virtual;

   end;

implementation

uses Unit1;


//-------- execute (protected) -----------------------------------------
procedure copy.execute;

var
title:String;
filename:String;
readm3u:String;
extension:String;
InFile : TextFile;
Position:Integer;



begin
while not Terminated do begin


error_notfound:=0;
assignFile (InFile, pathm3u);
reset (InFile);


Form1.Memo1.Lines.Add('['+TimeToStr(Time)+'] '+'Exportiere Datein, bitte warten...');

while not eof (InFile) do
  begin
  readln (InFile, readm3u);
  Position := Pos(':', readm3u);
  //Filename zusammenbasteln
  if Position <> 0 then begin
    extension:=ExtractFileExt(readm3u);
    filename:=pathcopyto+title+extension;

   //Kopiervorgang

      if Form1.CheckBox1.Checked=True then begin
        if not FileExists(PChar(readm3u)) then begin
          Form1.Memo1.Lines.Add('Die Datei '+PChar(readm3u)+' konnte nich gefunden werden');
          error_notfound:=error_notfound+1;
        end
        else begin
          CopyFile( PChar(readm3u) ,PChar(filename),false);
        end;
      end
      else begin
        if not FileExists(PChar(readm3u)) then begin
          Form1.Memo1.Lines.Add('Die Datei '+PChar(readm3u)+' konnte nich gefunden werden');
          error_notfound:=error_notfound+1;
        end
        else begin
          CopyFile( PChar(readm3u) ,PChar(filename),true);
        end;
   end;

  // progressbar Balken
  count:=Round(count_temp);
  Form1.ProgressBar1.Max:=count;
  Form1.Progressbar1.Position := Form1.Progressbar1.Position + 1;

  end
  else begin
  title:=readm3u;

  end;

  end;//while end
CloseFile (Infile);
Self.Terminate;
end;// end terminate

Form1.Memo1.Lines.Add('['+TimeToStr(Time)+'] '+'Fertig');
end;

//-------- create (public) ---------------------------------------------
constructor copy.create;
begin
  inherited create(true); // CreateSuspended = true
  freeOnTerminate := true;
end;

//-------- Setpathm3u (public) ----------------------------------------------
procedure copy.Setpathm3u (ppathm3u: String);
begin
  pathm3u := ppathm3u;
end;

//-------- Setpathcopyto (public) ----------------------------------------------
procedure copy.Setpathcopyto (ppathcopyto: String);
begin
  pathcopyto := ppathcopyto;
end;


end.


Könnte ja sein das ich nen neuen Fehler eingebaut hab ;-)

Gruß Seji
dummzeuch
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 593
Erhaltene Danke: 5


Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
BeitragVerfasst: Do 15.10.09 22:04 
user profile iconseji hat folgendes geschrieben Zum zitierten Posting springen:
Oh stimmt das ist ja mega peinlich ....

Das kann selbst alten Hasen noch passieren...

Aber:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
Form1.Memo1.Lines.Add('['+TimeToStr(Time)+'] '+'Exportiere Datein, bitte warten...');
      if Form1.CheckBox1.Checked=True then begin
          Form1.Memo1.Lines.Add('Die Datei '+PChar(readm3u)+' konnte nich gefunden werden');
          Form1.Memo1.Lines.Add('Die Datei '+PChar(readm3u)+' konnte nich gefunden werden');
  Form1.ProgressBar1.Max:=count;
  Form1.Progressbar1.Position := Form1.Progressbar1.Position + 1;
Form1.Memo1.Lines.Add('['+TimeToStr(Time)+'] '+'Fertig');


Diese Zeilen duerfen in einer Execute-Methode eines Threads nicht stehen. Jeglicher Zugriff auf die VCL ist verboten, er darf nur im Haupt-Thread stattfinden, z.B. via Synchronize.

twm
Lossy eX
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1048
Erhaltene Danke: 4



BeitragVerfasst: Fr 16.10.09 09:35 
Terminate: Na ja. Die Methode Terminate setzt einen Wert der innerhalb des Threads (Execute) abgefragt werden kann und sollte. Also die Eigenschaft Terminated. Du musst diese Eigenschaft natürlich überall dort überprüfen wo du gerne abbrechen möchtest. Um den Thread zu beenden musst du nur die Methode Execute verlassen. Aktuell ist es in deinem Code so, dass du am Anfang überprüfst ob der Thread beendet wurde und ansonsten deine Liste mit Dateien kopierst. Wenn du sie kopiert hast, dann sorgst du mittels Terminate dafür, dass sich dein Thread beenden. Dadurch kennt dein Thread nur zwei Stellen an der er aussteigen kann. Bevor er irgendwas gemacht und nachdem er alles erlegt hast. Wenn du das Kopieren nur ein Mal ausführst geht das auch deutlich einfacher.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure copy.execute;
begin
  try
    // Liste laden
    try
      // so lange noch etwas in der Datei ist bzw der Thread arbeiten soll
      while not (terminated or eof (InFile)) do begin
        // Zeile aus der Liste lesen

        // Datei kopieren
      end;
    finally
      // Liste frei geben
    end;
  except
    // hier Fehler protokolieren
  end;
end;

Problem an dem Code ist aktuell, dass du nicht direkt weißt ob der Thread jetzt beendet wurde oder ob er die Liste komplett abgearbeitet hat. Alternativ zu solche einer Schleife kannst du auch folgendes machen. Je nachdem wie du das gerne haben willst.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
while not eof (InFile) do begin
  if Terminated then begin
    // irgendwo loggen, dass abgebrochen wurde
    exit;
  end;

  // Zeile aus der Liste lesen

  // Datei kopieren
end;


Was mir gerade noch aufgefallen ist. Damit kannst du später ganz böse auf die Nase fallen. Du setzt FreeOnTerminate innerhalb des Konstruktors deines Thread und benutzt außerhalb des Threads noch die Instanz des Threads. Dabei muss man sehr sehr genau aufpassen. Wenn die Methode Execute fertig ist wird der Thread automatisch frei gegeben. Wenn du dann noch mal auf die Instanz zugreifst (Anzahl/Liste der nicht kopierten Dateien oder so), dann gibts eine Zugriffsverletzung. Also entweder FreeOnTerminate setzen und den Thread komplett alleine laufen lassen oder FreeOnTerminate nicht setzen und dann den Thread selber per Hand freigeben. Dafür kannst du bei der letzten Methode aber sicher sein, dass deine Instanz dir nicht irgendwann unter den Fingern weggezogen wird.

Und ich kann dummzeuch nur zustimmen. Aus einem Thread solltest du nicht auf ein Fenster oder irgendwelchen Fensterelemente zugreifen. Die VCL ist nicht sicher und du kannst dir schon gut Probleme dabei einfangen. Allerdings hast du sehr sehr viele Statusinformationen. Wenn die alle über Synchronize ausgegeben werden solltest du etwas bedenken. Um so mehr Statusinformationen du ausgeben musst um so häufiger muss der Thread warten. Wenns zu viele werden, dann ist der Thread irgendwann nur noch mit Warten beschäftigt. Damit wird der nutzen von Threads geschmälert bis er gegen null tendieren kann. Das aber nur mal so am Rande. Das wird wahrscheinlich später noch mal interessant werden können.

_________________
Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
seji Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Fr 16.10.09 13:23 
Zitat:
Diese Zeilen duerfen in einer Execute-Methode eines Threads nicht stehen. Jeglicher Zugriff auf die VCL ist verboten, er darf nur im Haupt-Thread stattfinden, z.B. via Synchronize.


Ich hba das nun mal ausgelagert mit Synchronize(). Und siehe da schon lässt sich der Thread wunderbar beenden.

@Lossy eX: Vielen Dank für deine ausführliche erklärung. Ich hab dann auch deinen zweiten Vorschlag umgesetzt, funktioniert wunderbar.

Also danke euch zwei, habt mir sehr geholfen. Schönes Wochenende.

Gruß Seji