![]() |
Thread .Terminate beim Beenden - Runtime Error 216
Ich habe in einem D2007 Programm sinngemäß folgenden Code:
Delphi-Quellcode:
Also der Thread läuft beim Starten einmal durch und legt sich dann selbst schlafen. Unter verschiedenen Umständen in meinem Programm wird der Thread ab und zu mal wieder aufgeweckt. Das funktioniert auch alles wunderbar und ohne Probleme.
TmyThread = class(TThread)
protected procedure Execute; override; public constructor Create(CreateSuspended: Boolean); destructor Destroy; override; end; constructor TmyThread.Create(CreateSuspended: Boolean); begin inherited Create(CreateSuspended); end; destructor TmyThread.Destroy; begin inherited; end; procedure TmyThread.Execute; begin while not Terminated do begin // mach irgend etwas, berechne self.Suspend; end; end; . . . procedure TfrmMain.FormCreate(Sender: TObject); begin myThread := TmyThread.Create(True); myThread.FreeOnTerminate := True; myThread.Resume; // Starte Thread und laufe einmal durch end; procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction); begin myThread.Terminate; if myThread.Suspended then begin myThread.Resume; end; // Was kann man hier einbauen um zu prüfen, ob der Thread beendet und zerstört ist end; Mein Problem ist allerdings, dass ab und zu beim Beenden vom Programm ein Runtime Error 216 kommt. Ich habe dann herausgefunden dass es am Thread liegt, bzw. dass er wohl noch nicht beendet und zerstört ist, das Hauptformular aber dann schon. Wenn ich das Programm über die IDE starte, dann kommt der Fehler fast gar nicht, wenn ich die EXE außerhalb der IDE starte, dann kommt der Fehler ziemlich oft. Ich habe auch schon folgendes probiert:
Delphi-Quellcode:
aber dann gibts ein Fehler bei WaitFor, dass die Thread ID nicht stimmt (oder so ähnlich).
procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin myThread.Terminate; if myThread.Suspended then begin myThread.Resume; end; myThread.WaitFor; // <----- neu end; Wenn ich folgendes mache:
Delphi-Quellcode:
dann gibt es nie einen Fehler, das gefällt mir aber nicht, einfach eine Pause da rein zu hauen.
procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin myThread.Terminate; if myThread.Suspended then begin myThread.Resume; end; sleep(50); // kurze Pause end; Folgendes bringt mich auch nicht weiter:
Delphi-Quellcode:
Das Problem dabei ist, dass myThread nach dem beenden und zerstören immer noch <> nil ist. Ich habe auch geprüft, dass beim Aufruf von Resume das Execute des thread verlassen wird und Destroy des Thread wird auch durchlaufen.
procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin myThread.Terminate; if myThread.Suspended then begin myThread.Resume; end; while myThread <> nil do begin // <----- neu sleep(50); Application.ProcessMessages; end; end; Habt Ihr eine Idee, was ich tun kann, wenn FreeOnTerminate := True und ich den Thread dann selbst terminiere, ich aber noch prüfen will, ob er wirklich beendet ist. Danke Sven |
Re: Thread .Terminate beim Beenden - Runtime Error 216
FreeOnTerminate = True in Kombination mit Resume ist eine ganz schlechte Idee...
Insbesondere, weil bei dir nach dem Suspend im Thread nix mehr passiert. Mehr dazu findest du hier: ![]() |
Re: Thread .Terminate beim Beenden - Runtime Error 216
Ja, den Beitrag hatte ich auch heute schon durchgekaut und jetzt aktuell noch mal.
Ich habe dann aus ![]()
Delphi-Quellcode:
in meinen Thread eingebaut und jetzt scheint es einwandfrei und ohen Fehler zu laufen.
// The same fix can be made in user's code at the end of Execute:
TMyThread.Execute; begin ... if FreeOnTerminate and (Suspended=True) then repeat Sleep(0); until (Suspended=False); end; Diesen Beitrag kann man sich auch noch mal in Bezug auf das Problem ansehen: ![]() Ansonsten sehe ich an meinem Code nicht unbedingt ein Problem. Trotdem schon mal Danke. Grüße Sven |
Re: Thread .Terminate beim Beenden - Runtime Error 216
Abgesehen, dass es nicht günstig ist "varBoolean=True" zu prüfen...
Wie soll das bitte zuverlässig funktionieren? Das sieht mir mehr nach einem Wettlauf aus, der mal klappt und mal (auf einem anderen Rechner) nicht. Und wieso kann man in einem Thread auf suspended abfragen? Entweder das ist true, dann läuft die Abfrage aber auch nicht, oder es ist false dann ist die Bedingung auch false. :gruebel: |
Re: Thread .Terminate beim Beenden - Runtime Error 216
Das kontrollierte Beenden von Threads kann Einem schon mal den Schlaf rauben. Ich verzichte grundsätzlich auf Resume/Suspend und arbeite stattdessen lieber mit Synchronisationsobjekten (Semaphoren). Weiterhin verzichte ich auf 'FreeOnTerminate' und gebe Threads immer kontrolliert frei. Tut man das nicht, kann es sein, das einem die Applikation beim Programmende um die Ohren fliegt. Im Finalization-Abschnitt von 'Classes.Pas' werden nämlich Resourcen freigegeben, die von der Terminierungssequenz eines Threads benötigt werden.
Ich schreibe mir immer eine Stop-Methode zu meinen Threads, damit der Thread kontrolliert terminiert. Danach kann ich den Thread beruhigt freigeben. Diese Stop-Methode macht Folgendes: 1. "Terminate" aufrufen. Damit wird ja nur das 'Terminated'-Flag gesetzt. 2. Sicherstellen, das die Execute-Methode terminiert. 3. Mit "WaitFor" warten, bis die Terminierungssequenz des Threads abgearbeitet ist. Da ich mit Semaphoren arbeite, sieht die Execute-Methode immer gleich aus;
Delphi-Quellcode:
Den Thread füttere ich mit Arbeit über eine AddJob-Methode, die den Job in die Queue schiebt und die Semaphore per 'ReleaseSemaphore' anschalte.
Procedure TMyThread.Execute;
Begin // Lokale Resourcen initialisieren, ggf. CoInitialize aufrufen, wenn mit COM gearbeitet wird Try While Not Terminated Do Begin WaitForSingleObject(fSemaphore,INFINITE); If Terminated Then Break; // Arbeit durchführen. Meist einen Job aus einer Queue holen und abarbeiten End; Finally // Lokale Resourcen wieder freigeben End End; Damit reicht es, unter (2) 'ReleaseSemaphore' aufzurufen, denn dann wacht der Thread auf, merkt das er terminieren soll und macht das auch.
Delphi-Quellcode:
Falls mein Thread noch mitten im Abarbeiten von Jobs steckt, die sich in der Queue angesammelt haben, macht das nichts. Er wird in jedem Fall nach Beendigung des aktuellen Jobs terminieren. Will ich das nicht, habe ich einen speziellen 'Ende-Job', den ich in die Queue schiebe. Dann werden zunächst alle Jobs abgearbeitet, dann kommt der Ende-Job und die 'Execute'-Methode hört auf.
Procedure TMyThread.Stop;
Begin Terminate; // Flag setzen ReleaseSemaphore(fSemaphore,1,nil); // 'Arbeit beenden' anfordern WaitFor; // Warten, bis Arbeit beendet ist End;
Delphi-Quellcode:
Von außen sieht die Arbeit mit meinen Threads also immer gleich aus: Erzeugen, mit Job füttern, Stop aufrufen, freigeben.
Procedure TMyThread.Stop;
Begin Add (TEndThreadJob.Create); // Ende-Job in die Queue schieben WaitFor; // Warten, bis Arbeit beendet ist End; Ach, warum ich nie mit Release/Suspend arbeite? Ich stell mir das so vor: Ich habe ja einen Arbeiter, der im Hintergrund Sachen für mich erledigt. Mit Suspend brate ich ihm eins über, sodaß er auf der Stelle ohnmächtig wird. Was ist aber, wenn er gerade in einer wichtigen Sache steckt? Blöd gelaufen. Ich muss also immer auf der Hut sein, ob und wann ich ihm eins verpasse. Es geht natürlich so, speziell wenn nur der Thread selbst sich eins vor die Rübe knallt, aber mir liegen diese Semaphoren einfach besser. Bildlich gesehen ackert mein Arbeiter im Nebenraum und ich schiebe ihm durch ein Fenster Zettel zu, auf denen steht, was er bei als nächstes zu tun hat. Irgendwie humaner, finde ich. Na ja. Arbeiter eingesperrt im Nebenraum, das ruft auch die Gewerkschaft auf den Plan... :mrgreen: |
Re: Thread .Terminate beim Beenden - Runtime Error 216
Zitat:
|
Re: Thread .Terminate beim Beenden - Runtime Error 216
Klar, nach dem ich mal eine Nacht drüber geschlafen habe, habe ich mich auch dazu entschlossen nicht mit FreeOnTerminate := True zu arbeiten, sondern ich kümmere mich selbst darum, wann der Thread zerstört wird. Da habe ich dann eine bessere Kontrolle, wann was passiert.
Das Suspend habe ich auch in den Thread selbst an das Ende der eigentlichen Arbeit gesetzt, um ihn gezielt schlafen zu legen. Damit habe ich auch die Kontrolle drüber. Wann das Resume aufgerufen wird, sollte dann doch eigentlich egal sein, da ja dann wieder von vorne begonnen wird. Das mit den Semaphoren muss ich mir dann auch bei Gelegenheit noch mal reinziehen, wenn ich mal ein paar Minuten Luft holen kann. @alzaimar: Du verwendest das ja auch im ![]() Jetzt habe ich aber noch eine Frage dazu mit diesen Jobs". Ich habe noch einen zweiten Thread den ich noch anpassen muss, der läuft alle 30 Sekunden wieder von vorne los. Stelle ich jetzt über einen Timer alle 30 Sekunden einen neuen Job in die Liste oder stelle ich den Job nur einmal rein und regele dann innerhalb des Jobs das Intervall. Aber wenn er dann ja nie beendet wird, wird auch den nächste Job nicht abgearbeitet???? Jetzt muss ich aber erst mal schauen, dass ich eine neue Softwareversion fertig bekomme, bin schon ein paar Tage überfällig, ansonsten springt mir bald jemand an die Gurgel. Erst mal Danke. Threads sind schon eine tolle Sache, wenn man es richtig programmiert und es dann auch funktioniert. Grüße Sven |
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:49 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 by Thomas Breitkreuz