| Autor |
Beitrag |
rushifell
      
Beiträge: 306
Erhaltene Danke: 14
|
Verfasst: 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.
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]; Mythread := BeginThread(nil, 0, @CheckUrl, Pchar(ThreadUrl), 0, ThreadId);
While MyThread<>0 do BEGIN 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 Gausi: Code- durch Delphi-Tags ersetzt
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 19.10.08 11:32
rushifell hat folgendes geschrieben : | | Ich muss MyThread:=0 setzen, sonst wird die While-Schleife nicht beendet. |
Dann benutze WaitForSingleObject zum Warten wie es sich gehört...
rushifell hat folgendes geschrieben : | | 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...
rushifell hat folgendes geschrieben : | | 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 
      
Beiträge: 306
Erhaltene Danke: 14
|
Verfasst: 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 
      
Beiträge: 306
Erhaltene Danke: 14
|
Verfasst: 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
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
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 END; Sleep(500); ThreadProgress:=i+1;
IF Terminated then Break;
END; end;
end. |
Und hier ein Auszug der Unit1.
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; end;
var Form1: TForm1; MyThread:TMyThread; Strglist:TStringlist; Fortschritt:Integer=0; MaxVal:Integer;
implementation
{$R *.dfm}
Procedure TForm1.ShowProgress; Begin IF Assigned(MyThread) then BEGIN MyThread.GetProgress(Fortschritt,MaxVal); Form1.Caption:=IntToStr(Fortschritt)+' '+IntToStr(MaxVal); END; End;
Procedure TForm1.Done(Sender:TObject); Begin ShowProgress; MyThread:=nil; ShowMessage('Fertig'); End;
procedure TForm1.Button1Click(Sender: TObject); begin IF not(Assigned(MyThread)) then BEGIN MyThread:=TMyThread.Create(Strglist); MyThread.OnTerminate:=Done; MyThread.Resume; END; end;
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;
procedure TForm1.Timer1Timer(Sender: TObject); begin IF Assigned(Mythread) then ShowProgress; end;
end. |
Gruß
rushifell
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 25.10.08 07:14
rushifell hat folgendes geschrieben : | | - 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 Delphi-Quelltext lieber 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...
rushifell hat folgendes geschrieben : | | 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 ( 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 
      
Beiträge: 306
Erhaltene Danke: 14
|
Verfasst: Sa 25.10.08 07:54
Danke jaenicke!
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.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| Constructor TMyThread.Create(Strg:TStringlist); Begin ThreadStrg:=TStringlist.Create; 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 
      
Beiträge: 306
Erhaltene Danke: 14
|
Verfasst: Sa 25.10.08 11:13
So müsste es doch richtig sein!?
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; 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
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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 
      
Beiträge: 306
Erhaltene Danke: 14
|
Verfasst: Sa 25.10.08 13:09
Danke für Deine Hilfe! 
|
|
|