AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Thread .Terminate beim Beenden - Runtime Error 216
Thema durchsuchen
Ansicht
Themen-Optionen

Thread .Terminate beim Beenden - Runtime Error 216

Ein Thema von SvB · begonnen am 12. Aug 2009 · letzter Beitrag vom 12. Aug 2009
Antwort Antwort
SvB

Registriert seit: 21. Okt 2004
Ort: Eckenroth
426 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#1

Thread .Terminate beim Beenden - Runtime Error 216

  Alt 12. Aug 2009, 00:41
Ich habe in einem D2007 Programm sinngemäß folgenden Code:

Delphi-Quellcode:
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;
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.
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:
procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  myThread.Terminate;
  if myThread.Suspended then begin
    myThread.Resume;
  end;
  myThread.WaitFor; // <----- neu
end;
aber dann gibts ein Fehler bei WaitFor, dass die Thread ID nicht stimmt (oder so ähnlich).

Wenn ich folgendes mache:
Delphi-Quellcode:
procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  myThread.Terminate;
  if myThread.Suspended then begin
    myThread.Resume;
  end;
  sleep(50); // kurze Pause
end;
dann gibt es nie einen Fehler, das gefällt mir aber nicht, einfach eine Pause da rein zu hauen.

Folgendes bringt mich auch nicht weiter:
Delphi-Quellcode:
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;
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.

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
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.648 Beiträge
 
Delphi 11 Alexandria
 
#2

Re: Thread .Terminate beim Beenden - Runtime Error 216

  Alt 12. Aug 2009, 00:59
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:
http://www.delphipraxis.net/internal....php?p=1064702
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
SvB

Registriert seit: 21. Okt 2004
Ort: Eckenroth
426 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#3

Re: Thread .Terminate beim Beenden - Runtime Error 216

  Alt 12. Aug 2009, 01:24
Ja, den Beitrag hatte ich auch heute schon durchgekaut und jetzt aktuell noch mal.
Ich habe dann aus http://qc.embarcadero.com/wc/qcmain.aspx?d=26291 den Fix
Delphi-Quellcode:
// 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;
in meinen Thread eingebaut und jetzt scheint es einwandfrei und ohen Fehler zu laufen.

Diesen Beitrag kann man sich auch noch mal in Bezug auf das Problem ansehen: Thread: Fatal Threading Model!

Ansonsten sehe ich an meinem Code nicht unbedingt ein Problem.

Trotdem schon mal Danke.

Grüße
Sven
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#4

Re: Thread .Terminate beim Beenden - Runtime Error 216

  Alt 12. Aug 2009, 09:09
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.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#5

Re: Thread .Terminate beim Beenden - Runtime Error 216

  Alt 12. Aug 2009, 09:42
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:
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;
Den Thread füttere ich mit Arbeit über eine AddJob-Methode, die den Job in die Queue schiebt und die Semaphore per 'ReleaseSemaphore' anschalte.

Damit reicht es, unter (2) 'ReleaseSemaphore' aufzurufen, denn dann wacht der Thread auf, merkt das er terminieren soll und macht das auch.
Delphi-Quellcode:
Procedure TMyThread.Stop;
Begin
  Terminate; // Flag setzen
  ReleaseSemaphore(fSemaphore,1,nil); // 'Arbeit beenden' anfordern
  WaitFor; // Warten, bis Arbeit beendet ist
End;
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.
Delphi-Quellcode:
Procedure TMyThread.Stop;
Begin
  Add (TEndThreadJob.Create); // Ende-Job in die Queue schieben
  WaitFor; // Warten, bis Arbeit beendet ist
End;
Von außen sieht die Arbeit mit meinen Threads also immer gleich aus: Erzeugen, mit Job füttern, Stop aufrufen, freigeben.


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...
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#6

Re: Thread .Terminate beim Beenden - Runtime Error 216

  Alt 12. Aug 2009, 09:48
Zitat von alzaimar:
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.
Genau deswegen, sollte dies der Thread immer selber tun. Man sagt ihm von aussen nur, dass er sich anhalten oder beenden soll und der Thread entscheidet dann von sich wann er dies tut.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
SvB

Registriert seit: 21. Okt 2004
Ort: Eckenroth
426 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#7

Re: Thread .Terminate beim Beenden - Runtime Error 216

  Alt 12. Aug 2009, 20:47
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 WorkerThread

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
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:02 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz