Autor Beitrag
Daimonion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 55



BeitragVerfasst: Do 10.07.08 15:50 
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:

ausblenden 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
  //starte/stoppe Thread
  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

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
procedure TThreadJoystick.Execute;
begin
  while not Terminated do
  begin
    //hier kommt dann der Code rein, der zyklisch ausgeführt wird.
    self.readJoystick;
    self.readEingaenge;
    postMessage(ParentHndl,WM_TJOYTHREAD_NEW_DATA_MESSAGE,0,0);
  end;
end;


Und hier noch der Vollständigkeit halber der Konstruktor
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
constructor TThreadJoystick.Create(values: PJoyValues; Parent: THandle);
begin
    //eigene Konstruktoraufgaben erledigen
    LoadIowKitAPI;
    IOWHndl := IowKitOpenDevice;
    myJoyValues := values;
    ParentHndl := Parent;
    //Parentkonstruktor aufrufen, damit es losgehen kann
    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.

ausblenden 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;
    //warte bis Thread beendet ist.
    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

_________________
Zitat Stromberg S03E08 - Erika:
"So...selbst die Capitolmatratze, die hier auf jedem Karosseplan umgerutscht ist, was will sie von mir? Zahlen. Aber so ist das. Als Chef in meiner Position, da bist du so einsam wie...Gott."
Xentar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2077
Erhaltene Danke: 2

Win XP
Delphi 5 Ent., Delphi 2007 Prof
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 55



BeitragVerfasst: 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?!

ausblenden 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
  //starte/stoppe Thread
  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;

_________________
Zitat Stromberg S03E08 - Erika:
"So...selbst die Capitolmatratze, die hier auf jedem Karosseplan umgerutscht ist, was will sie von mir? Zahlen. Aber so ist das. Als Chef in meiner Position, da bist du so einsam wie...Gott."


Zuletzt bearbeitet von Daimonion am Do 10.07.08 16:38, insgesamt 1-mal bearbeitet
Xentar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2077
Erhaltene Danke: 2

Win XP
Delphi 5 Ent., Delphi 2007 Prof
BeitragVerfasst: Do 10.07.08 16:36 
user profile iconDaimonion 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 55



BeitragVerfasst: Do 10.07.08 17:01 
user profile iconXentar 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.

_________________
Zitat Stromberg S03E08 - Erika:
"So...selbst die Capitolmatratze, die hier auf jedem Karosseplan umgerutscht ist, was will sie von mir? Zahlen. Aber so ist das. Als Chef in meiner Position, da bist du so einsam wie...Gott."
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Do 10.07.08 20:31 
user profile iconDaimonion 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.

_________________
Na denn, dann. Bis dann, denn.
delfiphan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: Sa 12.07.08 07:56 
Was auch noch ist:
ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 55



BeitragVerfasst: Mo 14.07.08 15:57 
user profile icondelfiphan hat folgendes geschrieben:
Was auch noch ist:
ausblenden 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:

ausblenden Delphi-Quelltext
1:
2:
3:
JoyThread.Terminate;
JoyThread.WaitFor;
JoyThread.Free;

_________________
Zitat Stromberg S03E08 - Erika:
"So...selbst die Capitolmatratze, die hier auf jedem Karosseplan umgerutscht ist, was will sie von mir? Zahlen. Aber so ist das. Als Chef in meiner Position, da bist du so einsam wie...Gott."