Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Thread verursacht Fehler obwohl beendet. (nur Direktstart)
Daimonion - Do 10.07.08 15:50
Titel: Thread verursacht Fehler obwohl beendet. (nur Direktstart)
Hallo Leute.
Ich versuche die Lösung auf ein Problem zu finden, wo mich am weiterarebeiten hindert.
Ich habe eine Gui die auf Buttonklick einen Thread startet bzw, stoppt.
Hier der Code dafür:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| procedure TForm1.BtnactivateClick(Sender: TObject); begin if Assigned(JoyThread) then begin JoyThread.Terminate; JoyThread.WaitFor; JoyThread := nil; BtnActivate.Caption := 'Aktiviere Joystick'; end else begin try JoyThread := TThreadJoystick.Create(@JoyData,self.Handle); BtnActivate.Caption := 'Deaktiviere Joystick'; except showmessage('Fehler'); end; end; end; |
Das Execute im Thread sieht wie folgt aus
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| procedure TThreadJoystick.Execute; begin while not Terminated do begin self.readJoystick; self.readEingaenge; postMessage(ParentHndl,WM_TJOYTHREAD_NEW_DATA_MESSAGE,0,0); end; end; |
Und hier noch der Vollständigkeit halber der Konstruktor
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| constructor TThreadJoystick.Create(values: PJoyValues; Parent: THandle); begin LoadIowKitAPI; IOWHndl := IowKitOpenDevice; myJoyValues := values; ParentHndl := Parent; FreeOnTerminate := true; inherited Create(false); end; |
Der Thread ließt von einer Hardware Daten aus und nachdem er das getan hat, schreibt er eine Nachricht in die Mesagequeue, die woanders abgearbeitet wird.
Soweit funktioniert das im Debugger gut. Der Thread wird korrekt gestartet und beendet. Wenn ich das Programm beende prüfe ich natürlich auch noch ob der Thread läuft und beende ihn bei Bedarf.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin if Assigned(JoyThread) then begin JoyThread.Terminate; JoyThread.WaitFor; end; end; |
Funktioniert auch korrekt.
Was aber nicht funktioniert: Wenn ich das Programm ohne Entwicklungsumgebung starte, also mit Klick auf die .exe, den Thread aktiviere und wieder deaktiviere und dann das Programm beende, dann kommt immer der Fehlerberichtsdialog von Windows und ich muß das Programm über den Taskmanager beenden.
Könnt ihr mir dabei helfen und mir nen Tipp geben ob und was ich noch falsch mache?
Danke im Vorraus
Grüße
Daimonion
Xentar - Do 10.07.08 16:11
- Was hast du schon zur Fehleranalyse unternommen?
- das "Inherited" gehört im Konstruktor normalerweise an den Anfang, nicht als letzte Zeile.
- Kommentier mal den Inhalt der Execute Methode aus, so dass nur noch die leere Schleife über bleibt, und probier es dann nochmal.
Daimonion - Do 10.07.08 16:29
- Was hast du schon zur Fehleranalyse unternommen?
Ich hab jegliche Funktion innerhalb des Threads und auch außerhalb des Threads einzeln auskommentiert um erst mal zu schauen ob ein Programmteil diesen Fehler verursacht.
- Kommentier mal den Inhalt der Execute Methode aus, so dass nur noch die leere Schleife über bleibt, und probier es dann nochmal.
Auch das hab ich alles gemacht. Der Fehler taucht dann trotzdem auf.
Ich hab die Vermutung das beim Beenden des Threads der Speicher falsch freigegeben wird. Unter Windows 2000 bekomme ich einen read Fehler an einer Speicherstelle.
- das "Inherited" gehört im Konstruktor normalerweise an den Anfang, nicht als letzte Zeile.
Danke für den Tipp. Hab ich ausgebessert.
Edit1: Es ist aber definitiv der Thread, denn wenn ich das Programm starte und beende ohne den Button geklickt zu haben, dann funktioniert dies korrekt.
Edit2: So, jetzt habe ich im Konstruktor des Thread FreeOnTerminate mal weggemacht (Default: false) und den Speicher in meinen Funktionen explizit mittles JoyThread.Free hinter JoyThread.Waitfor aufgerufen und siehe da, es funktioniert.
Nun nur noch die Frage, warum der Speicher nicht korrekt freigegeben wird, wenn ich FreeOnTerminate := true habe?!
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| procedure TForm1.BtnactivateClick(Sender: TObject); begin if Assigned(JoyThread) then begin JoyThread.Terminate; JoyThread.WaitFor; JoyThread.Free; JoyThread := nil; BtnActivate.Caption := 'Aktiviere Joystick'; end else begin try JoyThread := TThreadJoystick.Create(@JoyData,self.Handle); BtnActivate.Caption := 'Deaktiviere Joystick'; except on E: EAccessViolation do begin showmessage('Der Thread konnte nicht gestartet werden, weil die Treiber DLL nicht vorhanden ist.'); end; end; end; end; |
Xentar - Do 10.07.08 16:36
Daimonion hat folgendes geschrieben: |
| Unter Windows 2000 bekomme ich einen read Fehler an einer Speicherstelle. |
Na das ist doch mal was.
Starte das Programm mal aus Delphi heraus mit F8 und dann:
Suchen -> Laufzeitfehler suchen (In neueren Delphi Versionen: Zu Adresse springen) -> Adresse reinhacken -> OK. Normalerweise sollte der Debugger dir nun die Stelle anzeigen, wo der Fehler auftritt.
Daimonion - Do 10.07.08 17:01
Xentar hat folgendes geschrieben: |
Na das ist doch mal was.
Starte das Programm mal aus Delphi heraus mit F8 und dann:
Suchen -> Laufzeitfehler suchen (In neueren Delphi Versionen: Zu Adresse springen) -> Adresse reinhacken -> OK. Normalerweise sollte der Debugger dir nun die Stelle anzeigen, wo der Fehler auftritt. |
Hmm, so funnktioniert das bei mir irgendwie noch nicht. Der ReadFehler erfolgt unter Windows 2000 mein Zugriff auf 0x00e92b1f. Unter WinXP hab ich mit VS2005 (wurde einmal aus versehen als Debugger gestartet) zeigts mir an das eine Ausnahme an Adresse 0x00f82b1f aufgetreten ist.
Wenn ich unter Delphi 2007 das Programm starte (F8 führt ja nicht gleich das ganze Programm aus, sondern arbeitet gleich schrittweise) und die Adresse eingebe, dann komme ich im CPU Fenster immer auf die Stelle wo gerade der Debugger steht.
Was ich da noch als Problem sehe, sind die Adressen. Die sind ja nicht gerade klein. Denke das ist nur eine Speicherstelle einer Variable...
Wie im letzten Beitrag schon geschrieben, habe ich den Fehler vorerst beseitigen können indem ich FreeoOnTerminate nicht auf true setze und den Thread selber freigebe.
alzaimar - Do 10.07.08 20:31
Daimonion hat folgendes geschrieben: |
| Wie im letzten Beitrag schon geschrieben, habe ich den Fehler vorerst beseitigen können indem ich FreeoOnTerminate nicht auf true setze und den Thread selber freigebe. |
Das ist nicht nur "vorerst" beseitigt, sondern endgültig. Beim Thread.Destroy werden resourcen verwendet, die in Classes.pas deklariert sind und im finalization-Abschnitt der Unit freigegeben werden. Jeder Thread, der dann noch lebt, bringt die Anwendung zum Abschmieren.
Daher gilt: Threads immer im MainForm.Destroy, aber auf jeden Fall vor Eintritt in die Finalization-Abschnitte freigeben, dann schläft man besser.
delfiphan - Sa 12.07.08 07:56
Was auch noch ist:
Delphi-Quelltext
1: 2: 3:
| JoyThread.FreeOnTerminate := True; JoyThread.Terminate; <-- nach diesem Aufruf mit FreeOnTerminate kein Zugriff auf Objekt mehr erlaubt! JoyThread.WaitFor; <-- Objekt könnte schon längst freigegeben worden sein |
Entweder Terminate-WaitFor-Free oder FreeOnTerminate-Terminate. Beides zusammen geht nicht und ist nicht Sinn der Sache.
Daimonion - Mo 14.07.08 15:57
delfiphan hat folgendes geschrieben: |
Was auch noch ist:
Delphi-Quelltext 1: 2: 3:
| JoyThread.FreeOnTerminate := True; JoyThread.Terminate; <-- nach diesem Aufruf mit FreeOnTerminate kein Zugriff auf Objekt mehr erlaubt! JoyThread.WaitFor; <-- Objekt könnte schon längst freigegeben worden sein |
Entweder Terminate-WaitFor-Free oder FreeOnTerminate-Terminate. Beides zusammen geht nicht und ist nicht Sinn der Sache. |
Stimmt, das könnte eine potentzielle Fehlerquelle geben.
Aber wenn ich FreeOnTerminate := true setze und dann im OnClose Eventhandler JoyThread.Terminate mache, wie kann ich dann sicherstellen, dass ich warte, bis der Thread beendet und der Speicher freigegeben ist?
Momentan hab ich ja FreeOnTerminate := false und im OnClose Eventhandler des Haupthread folgenden Code:
Delphi-Quelltext
1: 2: 3:
| JoyThread.Terminate; JoyThread.WaitFor; JoyThread.Free; |
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!