Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Prüfen ob Thread noch läuft (https://www.delphipraxis.net/85003-pruefen-ob-thread-noch-laeuft.html)

Schwedenbitter 24. Jan 2007 14:44


Prüfen ob Thread noch läuft
 
Hallo,

ich möchte mittels meines Programms Dateien verschieben. Da es viele Dateien sein können und ich das ganze über einen langsamen VPN-Tunnel machen muss, würde das Programm ewig still stehen. Aus diesem Grunde habe ich die Prozeduren für das Kopieren und Löschen der Dateien in einen Thread gepackt. So weit - so gut. Allerdings habe ich immer noch folgende Probleme:

Da das Verschieben im Thread stattfindet, könnte es passieren, dass der Benutzer das Fenster schließt und das Programm damit beendet. Dann bestünde aber die Gefahr, dass offene Dateien zurückblieben, was auf die Dauer gesehen sehr unschön wäre. Die Eigenschaft Terminated gibt mir leider nur zurück, ob der Thread beendet werden soll. Aber nicht ob und wann er es nach dem Abarbeiten seiner Aufgaben selbst macht. Andere brauchbare Eigenschaften konnte ich in der Hilfe nicht finden. Ich habe mir bisher über eine globale Variable geholfen. Diese wird vor Ausführung des Thread auf True gesetzt und im Thread nach dem letzten Befehl erst auf False.

Gibt es bessere Methoden und falls ja, welche?

Außerdem bekomme ich beim Beenden des Programms hin und wieder die Fehlermeldung Exception der Klasse EOSError aufgetreten / Systemfehler. Code: 1400 Ungültiges Fensterhandle.

Was hat das zu bedeuten?

Schließlich habe ich trotz Verwendung eines Thread immer noch nicht in den Griff bekommen, dass sich das Fenster flüssiger bewegen lässt. Mir ist klar, dass BlockRead und BlockWrite bewusst größere Datenmengen verarbeiten und bei einem schlechten Durchsatz Verzögerungen hervorrufen. Aber ich dachte, dass das bei Ausführung im Thread im Hintergrund liefe und man in Ruhe weiterarbeiten kann.

Was mache ich falsch?

Falls Bedarf besteht, kann ich auch Quellcode reinsezten. Ich bitte dann aber um Erklärung was gebraucht wird; 400 Zeilen sind zuviel.

[edit=SirThornberry]Titel korrigiert - Mfg, SirThornberry[/edit]

sirius 24. Jan 2007 15:05

Re: Prüfen ob Thread noch läuft
 
z.B. kann man das mit Klassenmethoden machen. Da kannst du jederzeit abfragen, ob noch ein Thread läuft, auch wenn das Objekt selber nicht mehr existiert:
Delphi-Quellcode:
type
  tmythread = class(TThread)
  private
    { Private-Deklarationen }
  protected
    procedure Execute; override;
  public
    class function isrunning:boolean;
  end;

implementation

var running:integer=0;
{}

procedure tmythread.Execute;
begin
  inc(running);
  { Thread-Code hier einfügen }
  dec(running);
end;

class function tmythread.isrunning:boolean;
begin
  result:=running>0;
end;
inc und dec kann man auch in den constructor/destructor schreiben.


Dein Fehler kennn ich nicht. Aber ich würde vermuten, dass der Thread noch nicht beendet ist, wenn du den Hauptthread schließt. Fragst du in execute regelmäßig die Variable terminated ab?

stahli 24. Jan 2007 15:14

Re: Prüfen ob Thread noch läuft
 
Hallo Schwedenbitter,

Auszug aus dem Delphi-Thread-Demo:
Delphi-Quellcode:
procedure TThreadSortForm.StartBtnClick(Sender: TObject);
begin
  RandomizeArrays;
  ThreadsRunning := 3;
  with TBubbleSort.Create(BubbleSortBox, BubbleSortArray) do
    OnTerminate := ThreadDone;
  with TSelectionSort.Create(SelectionSortBox, SelectionSortArray) do
    OnTerminate := ThreadDone;
  with TQuickSort.Create(QuickSortBox, QuickSortArray) do
    OnTerminate := ThreadDone;
  StartBtn.Enabled := False;
end;

procedure TThreadSortForm.ThreadDone(Sender: TObject);
begin
  Dec(ThreadsRunning);
  if ThreadsRunning = 0 then
  begin
    StartBtn.Enabled := True;
    ArraysRandom := False;
  end;
end;
Stahli

shmia 24. Jan 2007 16:25

Re: Prüfen ob Thread noch läuft
 
Wenn ein Thread seine Aufgaben nicht ordnungsgemäss oder vollständig erfüllen konnte,
sollte man im Thread das Property ReturnValue auf einen Fehlercode setzen.
Am besten speichert man alle Threads, die man erzeugt hat, in einer Liste (TObjectList).
Im Event OnTerminate sorgt man dann dafür, dass der Thread aus der Liste entfernt wird.

Durch das Speichern aller Threads in der Liste hat man die volle Kontrolle und kann auch jederzeit die Anzahl der laufenden Threads ermitteln.

Pfoto 24. Jan 2007 16:51

Re: Prüfen ob Thread noch läuft
 
Hi!

Wenn das Abarbeiten der Aufgabe im Thread das Hauptfenster immer noch teilweise zum Stehen bringt
oder etwas ruckelt, dann deutet das noch auf eine nicht korrekte Lösung hin.
Ist jetzt schwer etwas von hier zu vermuten...


Aber noch zum Beenden:

Probier das hier im OnCloseQuery Ereignis des Forms:
Delphi-Quellcode:
with myThread do
begin
  Terminate;
  WaitFor;
  Free;
end;
FreeOnTerminate muss aber dann anfangs False sein.


Gruß
Pfoto

Schwedenbitter 24. Jan 2007 20:58

Re: Prüfen ob Thread noch läuft
 
Herzlichen Dank für die schnellen Antworten. Ich habe es jetzt mit dem Quellcode von sirius gemacht. Das funktioniert prima. Auf eine Liste verzichte ich, weil es nur einen Thread gibt. Werde mir das aber merken, falls ich mal was schwierigeres programmieren muss.

@Pfoto Ich will nicht behaupten, dass mein Code fehlerfrei ist. Schließlich bin ich absoluter Gelegenheitsprogrammierer und nehme Delphi nur, um mir die Arbeit auf Arbeit etwas einfacher zu machen. Allerdings habe ich Quelle und Ziel auf dieselbe Festplatte gelegt und eine 150 MB Datei für Versuchszwecke genommen. Fenster ruckelt kein bisschen. Liegt also wirklich am VPN. Was will man auch von 250 KBit/s upload bei DSL erwarten.
Den Quellcode habe ich probiert. Bei mir dauert es damit ca. 4 Sekunden, bis das Programm beendet ist, nachdem ich auf das Kreuz geklickt habe.

Ich habe jetzt auch die Quelle für die Exception gefunden. Allerdings weiß ich nicht, woran es wirklich liegt.
Ich hatte nach entsprechendem Quelltext aus dem Forum versucht, eine ProgressBar in eine Statusbar zu integrieren. Ich hatte zuerst die ProgressBar völlig gelöscht. Und siehe da, der Fehler trat nicht mehr auf. Anschließend ließ ich die ProgressBar drin. Allerdings separat und nicht mehr integiert. Der Fehler trat wieder auf. Jetzt ist sie ganz raus. Aber der Benutzer sieht jetzt leider nicht mehr, wie weit das Programm fortgeschritten ist. Schade.

alzaimar 24. Jan 2007 21:13

Re: Prüfen ob Thread noch läuft
 
Was ist denn bei dem Code von sirius, wenn man mehrere Threads startet? Dann klappt das nämlich nicht. Die Idee ist zwar ganz ok, aber eben nicht vollständig richtig.

sirius 25. Jan 2007 07:26

Re: Prüfen ob Thread noch läuft
 
Zitat:

Zitat von alzaimar
Was ist denn bei dem Code von sirius, wenn man mehrere Threads startet? Dann klappt das nämlich nicht. Die Idee ist zwar ganz ok, aber eben nicht vollständig richtig.

Jep, richtig, war ja auch nur so ein Ansatz.
Ich kann ja nicht immer Ansätze liefern, die komplett auf alle Probleme passen :zwinker:

Schwedenbitter 25. Jan 2007 07:32

Re: Prüfen ob Thread noch läuft
 
Zitat:

Zitat von alzaimar
Was ist denn bei dem Code von sirius, wenn man mehrere Threads startet?

Eigentlich sollte es auch mit mehreren Threads funktionieren. Wird einer gestartet, dann erhöht sich der Zähler um 1. Wird einer beendet, dann reduziert er sich wieder um 1. Macht im Ergebnis Null, wenn alle beendet sind. Dabei spielt es keine Rolle, in welcher zeitlichen Abfolge die Threads ablaufen.
Man muss meiner Meinung nach nur dafür sorgen, dass man bei den Terminated-Abfragen vor dem Beenden mitten im Thread auch eins runter zählt. Dann sollte es doch stimmen, oder?
Delphi-Quellcode:
procedure tmythread.Execute;
begin
  inc(running);
  { Thread-Code hier einfügen }
  if terminated then
  begin
    dec(running);
    exit;
  end;
  { Thread-Code-Ende }
  dec(running);
end;
Denn die Funktion IsRunning prüft nur, ob der Zähler Null ist.

sirius 25. Jan 2007 07:38

Re: Prüfen ob Thread noch läuft
 
Zitat:

Zitat von Schwedenbitter
Zitat:

Zitat von alzaimar
Was ist denn bei dem Code von sirius, wenn man mehrere Threads startet?

Eigentlich sollte es auch mit mehreren Threads funktionieren. Wird einer gestartet, dann erhöht sich der Zähler um 1. Wird einer beendet, dann reduziert er sich wieder um 1. Macht im Ergebnis Null, wenn alle beendet sind. Dabei spielt es keine Rolle, in welcher zeitlichen Abfolge die Threads ablaufen.
Man muss meiner Meinung nach nur dafür sorgen, dass man bei den Terminated-Abfragen vor dem Beenden mitten im Thread auch eins runter zählt. Dann sollte es doch stimmen, oder?

Denn die Funktion IsRunning prüft nur, ob der Zähler Null ist.

Ja das ist auch wieder richtig. Ich meinte ja: Je nach Anwendungsfall.
Ein Problem kriegt man z.B. mit dieser Variante, wenn deine Threads mit "freeonterminate=TRUE" laufen. Dann werden sie automatisch aus dem Speicher entfernt:
Wenn z.b ThreadInstanz1 bereits fertig ist und ThreadsInstanz2 noch läuft dann gibt dir "isRunning" True zurück und wenn du dann ThreadInstanz1.waitfor aufrufst, kriegts du eine Zugriffsverletzung.

sirius 25. Jan 2007 07:45

Re: Prüfen ob Thread noch läuft
 
Zitat:

Zitat von Schwedenbitter
Ich habe jetzt auch die Quelle für die Exception gefunden. Allerdings weiß ich nicht, woran es wirklich liegt.
Ich hatte nach entsprechendem Quelltext aus dem Forum versucht, eine ProgressBar in eine Statusbar zu integrieren. Ich hatte zuerst die ProgressBar völlig gelöscht. Und siehe da, der Fehler trat nicht mehr auf. Anschließend ließ ich die ProgressBar drin. Allerdings separat und nicht mehr integiert. Der Fehler trat wieder auf. Jetzt ist sie ganz raus. Aber der Benutzer sieht jetzt leider nicht mehr, wie weit das Programm fortgeschritten ist. Schade.

:gruebel:
Könnte es sein, dass du die Progressbar ohne synchroinize aufrufst?


Edit: Da war ein Formatfehler :drunken: Man sollte doch öfter mal den Vorschau-Button wählen...

Schwedenbitter 25. Jan 2007 08:00

Re: Prüfen ob Thread noch läuft
 
Zitat:

Zitat von sirius
:gruebel:
Könnte es sein, dass du die Progressbar ohne synchroinize aufrufst?

Ja, ich habe so drauf zugegriffen, wie ich es immer getan habe. Ich habe schon in mehreren Programmen eine ProgressBar benutzt, ohne jemals Fehlermeldungen gehabt zu haben. Deshalb ging ich (zunächst) davon aus, dass es an der Integration in die StatusBar liegen würde.

Was muss ich für den Aufruf mit synchronize tun? Ich würde gern die StatusBar wieder drin haben; ohne Fehlermeldung versteht sich. Entschuldigt die blöde Frage.

sirius 25. Jan 2007 08:12

Re: Prüfen ob Thread noch läuft
 
[/delphi]Jede Interaktion mit dem Hauptthread muss mit synchronize erfolgen.
Und damit erst recht jeder Zugriff auf VCL-Komponenten.

Am Besipiel progressbar (dies gilt für alle anderen Variablen, Komponenten aus dem Haptthread genauso):
Delphi-Quellcode:
type
  tmythread = class(TThread)
  private
    { Private-Deklarationen } 
    incProgressbar:integer;
  protected
    procedure Execute; override; //in Execute kommen nur Sachen rein, die definitv nie auf Komponenten oder Variablen anderer Threads zugreifen
    procedure updateProgressbar; //immer eigene Methoden für solche "Querzugriffe"
  public
  end;

implementation

procedure tmyThread.UpdateProgressbar;
begin
  //hier möglichst zeitsparend (wenig befehle) arbeiten, sonst bringt der ganze Thread nix
  progressbar.stepby(incProgressbar);
end;

procedure tmythread.Execute;
begin
  //...
  incprogressbar:=5;
  synchronize(updateprogressbar); //Variablen kannst du am einfachsten über die private Deklaration des ThreadObjektes übergeben
  //...
end;

Schwedenbitter 25. Jan 2007 23:30

Re: Prüfen ob Thread noch läuft
 
Vielen, vielen Dank!
Ich habe jetzt meinen Quellcode gesäubert. Die Fehlermeldung ist weg und selbst beim wirklich lahmen VPN kann ich jetzt das Fenster bewegen, als ob ich auf meiner Festplatte arbeiten würde.
Meine Fragen sind jetzt alle geklärt.

shmia 26. Jan 2007 13:20

Re: Prüfen ob Thread noch läuft
 
Wichtiger Hinweis: man sollte InterlockedDecrement aus Unit Windows verwenden
Delphi-Quellcode:
Dec(ThreadsRunning); // nicht Threadsafe !!!
if ThreadsRunning=0 then ...

if InterlockedDecrement(ThreadsRunning) = 0 then ... // so ist's richtig

sirius 26. Jan 2007 13:39

Re: Prüfen ob Thread noch läuft
 
Zitat:

Zitat von shmia
Wichtiger Hinweis: man sollte InterlockedDecrement aus Unit Windows verwenden
Delphi-Quellcode:
Dec(ThreadsRunning); // nicht Threadsafe !!!
if ThreadsRunning=0 then ...

if InterlockedDecrement(ThreadsRunning) = 0 then ... // so ist's richtig

:gruebel: Hmm, aber es gibt doch keinen anderen thread, der auf meine Variable zugreift?

Luckie 26. Jan 2007 14:06

Re: Prüfen ob Thread noch läuft
 
Warum arbeitest du nicht mit Ereignissen? Wenn ein Thread gestartet wird, wird ein Ereignis ausgelöst und wenn ein Thread sich beendet, wird ein Ereignis ausgelöst. So kannst du in deinem Formular immer schön mitzählen.

shmia 26. Jan 2007 14:08

Re: Prüfen ob Thread noch läuft
 
Zitat:

Zitat von sirius
:gruebel: Hmm, aber es gibt doch keinen anderen thread, der auf meine Variable zugreift?

Na wenn's nur einen Thread gibt, dann braucht man auch keinen Zähler. Aber bei mehr als einem Thread kanns passieren, dass der Assemblerbefehl DEC in der Mitte unterbrochen wird.
Greift dann ein weiterer Thread auf die Variable zu, wird's gefährlich denn das erzeugt bösartige und nicht reproduzierbare Bugs.

Daimonion 9. Feb 2009 10:34

Re: Prüfen ob Thread noch läuft
 
Hallo

Ich habe noch eine Frage zu Sirius Beispiel im zweiten Post.

Die Funktion is running hat ja das class attribut vorne dran.
Soweit ich weiß wird die Funktion dadurch statisch gemacht.

Kann ich dann von außerhalb diese Funktion aufrufen, ohne dass ich die Threadklasse instanziiert habe?

himitsu 9. Feb 2009 11:25

Re: Prüfen ob Thread noch läuft
 
jupp, kannst du ... einfach über tmythread.isrunning , :angel:
wobei ich diese globale Variable noch in eine Klassenvariable umwandeln würde
Delphi-Quellcode:
type
  tmythread = class(TThread)
  private
    { Private-Deklarationen }
    class var running: integer = 0;
  protected
    procedure Execute; override;
  public
    class function isrunning: boolean;
  end;

Daimonion 9. Feb 2009 11:26

Re: Prüfen ob Thread noch läuft
 
Jup macht Sinn. Danke. Kann das hier nämlich noch nicht testen, da ich das Zielsystem nicht hier hab.

Danke für die Antwort.

Grüße
Daimonion

Luckie 9. Feb 2009 20:08

Re: Prüfen ob Thread noch läuft
 
Man könnte sich auch einfach das Handle des Threads merken und wenn nötig den Exitcode abfragen: MSDN-Library durchsuchenGetExitCodeThread.

Daimonion 9. Feb 2009 20:15

Re: Prüfen ob Thread noch läuft
 
Aauch ne gute Idee.

Ursprünglich wollte ich den Thread auch mit den üblichen Wrapperfunktionen BeginThread und EndThread machen.

Mittlerweile bin ich froh, das ich für den Thread ne gekapselte Klasse habe. Das ist schon wieder sooo viel dazu gekommen... ;)


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:18 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz