Autor Beitrag
rushifell
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 306
Erhaltene Danke: 14



BeitragVerfasst: So 19.10.08 10:39 
Hallo Leute,

ich habe seit langem mal wieder eine Frage, meistens werde ich ja über die Suchfunktion fündig.

Bin mir auch nicht sicher, ob ich hier richig gepostet hab.

Ich möchte bei einer Liste von Urls überprüfen, ob die Urls gültig sind. Leider dauert das stets ein paar Millisekunden bis Sekunden, während der ich kein Zugriff auf mein Formular habe, es sozusagen "einfriert". Ich kam auf die Idee, die Function zur Überprüfung der Ulr einfach in einen Thread auszulagern.

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:
Var 
  ThreadResult:Boolean;
  MyThread:Integer;

Function CheckUrl(url:String):Integer;
Begin
 Result:=0;
 ...
 ThreadResult:=True/False
 MyThread:=0;
End;

procedure TForm1.Button1Click(Sender: TObject);
Var i:Integer;
    ThreadId:Cardinal;
    ThreadUrl:String;
begin

For i:=0 to UrlStringlist.Count-1 do

    BEGIN 
     ThreadUrl:=UrlStringlist.Strings[i]; //z.B. 'http://www.website.com'

     Mythread := BeginThread(nil, 0, @CheckUrl, Pchar(ThreadUrl), 0, ThreadId);

     While MyThread<>0 do
           BEGIN
            //Ausgabe der bereits verstrichenen Zeit
           END;

     IF not(ThreadResult) then
        ListBox1.Items.Add('Url existiert nicht');
         
     CloseHandle(MyThread);

    END;
     
end;


In der Schleife While MyThread<>0 wird die bereits verstrichene Zeit angezeigt, während die Überprüfung der Url läuft. Hier soll einfach die Zeit überbrückt werden, in der CheckUrl arbeitet.

Das Programm funktioniert so einwandfrei. Ich befürchte aber, dass der Code nicht ganz sauber ist.

Was bringt mir Result:=0 in der Function CheckUrl
Ich muss MyThread:=0 setzen, sonst wird die While-Schleife nicht beendet.
Kann ich den Rückgabewert aus der Function CheckUrl ob die Url existiert abfragen, ohne die globale Variable ThreadResult zu benutzen?

Das Tutorial über Threads von Luckie hab ich bereits durchgearbeitet.

Meine Frage ist eben, ist das so in Ordnung? Was sollte ich verbessern. Mit der Thread-Klasse möchte ich nicht unbedingt arbeiten, weil ich denke, es wäre für soetwas einfach zu aufwendig. Ich hoffe, ihr könnt mir helfen.

Danke schonmal

Moderiert von user profile iconGausi: Code- durch Delphi-Tags ersetzt
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 19.10.08 11:32 
user profile iconrushifell hat folgendes geschrieben Zum zitierten Posting springen:
Ich muss MyThread:=0 setzen, sonst wird die While-Schleife nicht beendet.
Dann benutze WaitForSingleObject zum Warten wie es sich gehört...
user profile iconrushifell hat folgendes geschrieben Zum zitierten Posting springen:
Kann ich den Rückgabewert aus der Function CheckUrl ob die Url existiert abfragen, ohne die globale Variable ThreadResult zu benutzen?
Frage einfach den Exitcode ab, den du damit setzt.

Beides steht im Tutorial unter 3.4 im Beispiel drin...

user profile iconrushifell hat folgendes geschrieben Zum zitierten Posting springen:
Meine Frage ist eben, ist das so in Ordnung? Was sollte ich verbessern. Mit der Thread-Klasse möchte ich nicht unbedingt arbeiten, weil ich denke, es wäre für soetwas einfach zu aufwendig.
Ich denke die Klasse TThread wäre hier schon gut geeignet, denn dann kannst du einfach alle Überprüfungen in diesem einen Thread durchführen und ggf. die Fortschrittsanzeige mit dem Formular synchronisieren.

So musst du jedesmal einen Thread starten und auf die Beendigung warten, das finde ich aufwendig. Aber solange du es korrekt umsetzt... Naja, du musst es wissen wie du es machst.
rushifell Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 306
Erhaltene Danke: 14



BeitragVerfasst: So 19.10.08 15:32 
Danke für Deine Antwort!
jaenicke hat folgendes geschrieben:

Dann benutze WaitForSingleObject zum Warten wie es sich gehört...

Das hatte ich bereits probiert. Beim Verwenden von WaitForSingleObject friert bei mir das Formular solange ein, bis der Thread beendet ist, ebenso beim OnWait-Ereignis eines Thread-Objects. Vielleicht mache etwas falsch? Ich möchte eben vom Start bis zum Ende des Threads eine Ausgabe auf meinem Formular haben, ansonsten würde der Thread für mich keinen Sinn machen.

Ich bin auch nicht sehr davon begeistert, für jeden Schleifendurchlauf einen eigenen Thread zu erstellen. Wie kann ich den Status des Threads abfragen? Muss ich dazu eigene globale Variablen benutzen oder gibts da eine andere Möglichkeit?
rushifell Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 306
Erhaltene Danke: 14



BeitragVerfasst: Sa 25.10.08 06:51 
Hallo,

hab das ganze jetzt, wie von jaenicke vorgeschlagen, mit der Thread-Klasse gelöst. Da ich mich mit Threads überhaupt nicht auskenne, wollte ich nochmal fragen, ob das so in Ordnung ist oder jemand noch Verbesserungsvorschläge hat. Vielleicht hilft's ja auch irgend jemandem weiter. Danke schonmal.

Ziel war es, wie oben schon beschrieben, während eines Verarbeitungsvorgangs das Einfrieren des Formulars zu verhindern, so dass ich z.B. einen Button zum Abbrechen des Vorgangs anklicken kann. Wenn ich z.B. ein Sleep(5000) aufrufen würde, wäre mein Formular für 5 Sekunden "eingefroren". Lagere ich das Sleep(5000) in einen Thread aus, dann wird dieses "Einfrieren" verhindert.

Das folgende Beispiel ist nur eine Demonstration, die ich in ähnlicher Form in mein Projekt übernommen habe. Geprüft wird, ob die Dateinamen der Stringlist (Strglist) existieren, und entsprechend wird darauf reagiert. In meinem Projekt überprüfe ich natürlich, ob eine Url existiert.

    - Sleep habe ich nur zur Demonstration der Fortschrittsanzeige eingefügt.
    - DoOutput ist eine Procedure in der irgendetwas verarbeitet wird.
    - MyThread.Free funktioniert bei mir nicht, deshalb habe ich MyThread:=nil gesetzt.

Noch eine Frage zum Synchronize:
Das benutze ich doch eigentlich nur für visuelle Komponenten, also bei der Verarbeitung der Stringlist überflüssig?

Hier die Thread-Unit
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:
unit Unit2;

interface

uses
  Classes, SysUtils;

type
  TMyThread = class(TThread)
  private
    ThreadStrg:TStringlist;
    ThreadProgress:Integer;
    ThreadMaxCount:Integer;
  protected
    Procedure Execute; override;
  public
    Procedure GetProgress(Var actpos,maxval:Integer);
    Constructor Create(Var Strg:TStringlist);
  end;

implementation

//Abfrage des Fortschritts in Unit1
Procedure TMyThread.GetProgress(Var actpos, maxval:Integer);
Begin
 Actpos:=ThreadProgress;
 Maxval:=ThreadMaxCount;
End;


Constructor TMyThread.Create(Var Strg:TStringlist);
Begin
 ThreadStrg:=Strg;
 ThreadProgress:=0;
 ThreadMaxCount:=ThreadStrg.Count;

 FreeOnTerminate := True;
  inherited Create(True);
End;


procedure TMyThread.Execute;
Var i:Integer;
begin

 For i:=0 to ThreadStrg.Count-1 do
     BEGIN
     IF FileExists(ThreadStrg.Strings[i]) then
        BEGIN
         //DoOutput
         //Synchronize(DoOutput);
        END;
     Sleep(500); //Nur zur Demo
     ThreadProgress:=i+1;

     IF Terminated then
        Break;

    END;
end;

end.


Und hier ein Auszug der Unit1.

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:
unit Unit1;

interface
...
  public
   Procedure Done(Sender:TObject);
   Procedure ShowProgress;
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;
  MyThread:TMyThread;
  Strglist:TStringlist;
  Fortschritt:Integer=0;
  MaxVal:Integer;

implementation

{$R *.dfm}

//Zeigt den Fortschritt des Threads
Procedure TForm1.ShowProgress;
Begin
 IF Assigned(MyThread) then
    BEGIN
     MyThread.GetProgress(Fortschritt,MaxVal);
     Form1.Caption:=IntToStr(Fortschritt)+' '+IntToStr(MaxVal);
    END;
End;

//Thread wurde beendet
Procedure TForm1.Done(Sender:TObject);
Begin
 ShowProgress;
 MyThread:=nil; //MyThread.Free friert Formular ein
 ShowMessage('Fertig');
End;

//Button zum Starten des Threads
procedure TForm1.Button1Click(Sender: TObject);
begin
 IF not(Assigned(MyThread)) then
    BEGIN
     MyThread:=TMyThread.Create(Strglist);
     MyThread.OnTerminate:=Done;
     MyThread.Resume;
    END;
end;

//Button zum Abbrechen des laufenden Threads
procedure TForm1.Button2Click(Sender: TObject);
begin
 IF Assigned(MyThread) then
   BEGIN
    MyThread.Suspend;
    IF MessageDlg('Wirklich abbrechen?',mtConfirmation,[mbYes,mbNo],0)=mrYes then
       MyThread.Terminate;
    MyThread.Resume;
   END;
end;

//Timer für die Fortschrittsanzeige
procedure TForm1.Timer1Timer(Sender: TObject);
begin
 IF Assigned(Mythread) then ShowProgress;
end;

end.


Gruß
rushifell
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 25.10.08 07:14 
user profile iconrushifell hat folgendes geschrieben Zum zitierten Posting springen:
- MyThread.Free funktioniert bei mir nicht, deshalb habe ich MyThread:=nil gesetzt.
Free darfst du auch gar nicht aufrufen, denn du setzt FreeOnTerminate auf True, das heißt der Thread zerstört sich nach der Ausführung selbst. Die Variable auf nil setzen tut dem Objekt nix sondern zerstört nur den Verweis darauf in der Variablen, das ist ok.

Du solltest statt
ausblenden Delphi-Quelltext
1:
ThreadStrg:=Strg;					
lieber
ausblenden Delphi-Quelltext
1:
ThreadStrg.Assign(Strg);					
benutzen. Denn ansonsten wird nur die Referenz auf dein Stringlistenobjekt kopiert, und ThreadStrg verweist auf die StringList außerhalb des Threads. Und so etwas kann ins Auge gehen...

user profile iconrushifell hat folgendes geschrieben Zum zitierten Posting springen:
Das benutze ich doch eigentlich nur für visuelle Komponenten, also bei der Verarbeitung der Stringlist überflüssig?
Das Synchronize ist eigentlich genau dafür da um zum Beispiel Form1.Caption bzw. einen Fortschrittsbalken o.ä. für die Fortschrittsanzeige zu verändern.
Was GetProgress angeht: Das Polling ist eigentlich gar nicht nötig, aber da du nur lesend zu greifst auf die Variablen im Thread wird es vermutlich nicht knallen.
Du könntest aber auch einfach alle paar Zeilen (
ausblenden Delphi-Quelltext
1:
2:
if i mod 10 = 0 then
  Synchronize(UpdateCaption);
für alle 10 Zeilen) aus dem Thread heraus die Fortschrittsanzeige aktualisieren. Eben mit Synchronize.
rushifell Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 306
Erhaltene Danke: 14



BeitragVerfasst: Sa 25.10.08 07:54 
Danke jaenicke!

ausblenden Delphi-Quelltext
1:
ThreadStrg.Assign(Strg);					

Das löst bei mir eine Exception aus.
Zitat:

Zugriffsfehler bei Adresse...
Lesen von Adresse...

Assign wäre mir auch lieber. Woran könnte das liegen?

Edit:
Manchmal hab ich doch wirklich ein Brett vor dem Kopf.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
Constructor TMyThread.Create(Strg:TStringlist);
Begin
 ThreadStrg:=TStringlist.Create; //Create hat gefehlt
 ThreadStrg.Assign(Strg);
 ThreadProgress:=0;
 ThreadMaxCount:=ThreadStrg.Count;

 FreeOnTerminate := True;
  inherited Create(True);
End;

Jetzt stellt sich mir aber wieder eine weitere Frage. Wohin packe ich das ThreadStrg.Free, den Desctructor überschreiben oder lieber ans Ende der Excecute Methode setzen?

Die Ausgabe mit Synchronize habe ich jetzt hinbekommen, danke :-)
rushifell Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 306
Erhaltene Danke: 14



BeitragVerfasst: Sa 25.10.08 11:13 
So müsste es doch richtig sein!?

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
public
 Constructor Create(Strg:TStrings);
 Destructor Destroy; override;
end;

...

Constructor TMyThread.Create(Strg:TStringlist);
Begin
 ThreadStrg:=TStringlist.Create; //Create hat gefehlt
 ThreadStrg.Assign(Strg);
 ThreadProgress:=0;
 ThreadMaxCount:=ThreadStrg.Count;

 FreeOnTerminate := True;
  inherited Create(True);
End; 

Destructor TMyThread.Destroy;
Begin
 ThreadStrg.Free;
End;


Sorry fürs viele Editieren :-(
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 25.10.08 12:54 
Ja, so ist es richtig, wobei es zwar egal ist wo du die StringList freigibst, aber so wie du es jetzt hast ist es IMHO sauberer, denn:
Du erzeugst sie im Konstruktor, dann sucht man nach dem Freigeben auch im Destruktor ;-).
rushifell Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 306
Erhaltene Danke: 14



BeitragVerfasst: Sa 25.10.08 13:09 
Danke für Deine Hilfe! :beer: