Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Thread abbrechen (https://www.delphipraxis.net/4740-thread-abbrechen.html)

OrallY 10. Mai 2003 12:29


Thread abbrechen
 
In einem Thread läuft ein langwieriger Prozess ab. Nun möchte ich dem User die Möglichkeit geben, den Prozess abzubrechen. Mit TThread.Terminate klappts natürlich nicht. Also habe ich mal TerminateThread ausprobiert. Manchmal hats damit geklappt, manchmal ist das Programm einfach abgestürtzt, dies war häufiger der Fall. Also habe ich eine Prozedure in der Thread-Klasse definiert, die so aussieht:
Code:
procedure TMyThread.ShutDownThread;
var
  ExitCode: Longword;
begin
  GetExitCodeThread(Handle, ExitCode);
  ExitThread(ExitCode);
end;
Wenn ich nun aber im Hauptthread diese Methode aufrufe, ist meine Anwedung niergens mehr zu sehen, scheint aber noch zu laufen, da in der IDE der "Play" Button noch gedrückt ist usw.. Welche Möglichkeit habe ich noch, einen einen langen Prozess in einem Thread abzubrechen oder ist die von mir verwendete Methode die richtige, nur mach ich was falsch?

Stream 10. Mai 2003 14:03

Hallo OrallY!

Das Problemm kenne ich, und es ist auch leicht zu lösen. Das könnte zum Beispiel ein Boolean-Wert in der Thread-Klasse sein, den gibst du den Namen Cancel und setzt ihn als Public Wert ein. Beim Start des Threades setzt du diesen Wert auf false. Wenn der User nun auf den Abbrechen-Button Klickt, wird der Cancel-Bool auf true gesetzt. Jetzt musst du nur noch in deiner Execute-Prozedur eine Abfrage erstellen, ob der Thread geschlossen werden soll oder nicht.

Das sieht dann in Etwa so aus:
Delphi-Quellcode:
procedure TMyThread.Execute;
begin
FreeOnTerminate := true;
Cancel := false;
...
if Cancel then begin
    Terminate;
    exit;
  end;
...
end;
Dein Problem dürfte dann nicht auftreten. Wenn du aber den Thread erneut starten willst musst du ihn aber erneut erstellen! Ansonsten gibt es ne dicke fette Fehlermeldung.

Tschö!

OrallY 11. Mai 2003 09:12

Ersteinmal danke für die Hilfe! Doch muss ich dich leider enttäuschen. Dein Lösungsweg hilft mir erstens nicht weiter und zweitens ist er sogar ein wenig umständlich. Ein kurze Erläuterung:
Die Mehode Terminate eines TThread-Klasse macht nichts anderes, als einen boolischen Wert inerhalb der Klasse auf true zu setzen. Also genau das, was du mit Cancel gemacht hast. Deswegen kannst du einfach von auserhalb die Methode Terminate aufrufen und dann im Thread-Source den boolischen Wert Terminated abfragen:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
begin
   MyThread.Terminate;
end;

procedure TMyThread.Execute;
begin
FreeOnTerminate := true;
...
if terminated then
* * exit;
...
end;
Trotzdem danke!

OrallY 11. Mai 2003 09:16

Mein Code sieht aber in etwa so aus:
Delphi-Quellcode:
procedure TMyThread.Execute;
begin
   Machwasverdammtlanges(beschäftigung); {kann mehrere Minuten dauern, bis die Prozedur wieder zurückkehrt}
end;
Irgendwie scheint ExitThread den Hauptthread gleich mit zu killen. Man kann ja auch keinen Handle übergeben... :?

jbg 11. Mai 2003 10:13

Du könntest eine Stille Exception auslösen und diese in der Execute-Funktion abfangen.

Delphi-Quellcode:
type
  EThreadExit = class(EAbort);

procedure TMyThread.HandleException
begin
 // System.ExceptObject liefert die Aktuelle Exception
  Application.ShowException(ExceptObject);
end;

procedure TMyThread.Execute;
begin
  try
    while (not Terminated) and (not Application.Terminated) do
    begin
      AktionenAusfuehren;
    end;
  except
    on E: EThreadExit do ; // nichts machen
    on E: Exception do
      Synchronize(HandleException);
  end;
end;

procedure TMyThread.AktionenAusfuehren;
begin
 // ...
  if ThreadAbbrechen then
    raise EThreadExit.Create;
 // ...
end;

OrallY 11. Mai 2003 13:09

Danke! Deine Lösung gefällt gut! Doch hab ich das Problem jetzt so gelöst, dass ich in die Prozeduren, die aufgerufen werden, eine Abfrage eingebaut hab: sowas wie [b]if DoCancel then exit;[b].
Aber deine Lösung werde ich mir auf jedenfall merken :wink: .

sakura 11. Mai 2003 13:17

Bitte nochmal für die langsameren unter uns. Warum kannst Du innerhalb des Threads nicht die Eigenschaft Terminated überprüfen? Wenn Du die Variable DoCancel testen kannst, müsste es mit der vorgesehenen Eigenschaft doch auch gehen. Andere Methoden könnten u.U. schneller zu Speicherleaks führen.

...:cat:...

OrallY 11. Mai 2003 17:33

Bei der Prozedur handelte es sich um eine "Komprimierungsprozedur", d.h. eine Prozedur die mit ZLib arbeitete (oder besser, einem Nachfolger). Da die Prozedur erst wieder zurückkehrte, nachdem der Stream komprimiert war, wäre eine Terminated-Abfrage sinnlos gewesen. Wenn ich den Thread einfach mit TerminateThread gekillt habe, konnte diese Prozedur aber auch keinen Müll mehr beseitigen, es führte zu abstürtzen. Nun habe ich die ZLib CompresionStream mit einem Boolischen-Wert erweitert, der, wenn er auf true gesetzt wird, die Prozedur abbricht. Dies war möglich, da die Komprimierungsfunktion in einer schnuckeligen Schleife ablief. Also musste ich nach jedem Schleifendurchlauf nur die Variable überprüfen, die dazugefügt habe. Von außerhalb musste ich also nur noch die Variable auf true bzw. false setzten, um den Vorgang abzubrechen, ohne Speicherleaks zu riskieren.

Alles klar? :wink:


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:06 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