[gelöst] Thread wieder frei geben

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

[gelöst] Thread wieder frei geben

Beitrag von Michl »

Hallo Allerseits,

ich würde gern wissen, ob ein von mir erzeugter Thread tatsächlich bei "FreeOnTerminated" wieder frei gegeben worden ist. Das sollte nach dem Wiki eigentlich so sein: "Eine andere wichtige Eigenschaft (property) von TThread ist FreeOnTerminate. Wenn diese Eigenschaft auf True gesetzt ist, wird der Thread automatisch freigegeben (wie ein Aufruf von .Free), wenn Execute beendet wird." Leider kann ich HeapTrc nicht einsetzen, aufgrund von Zugriffsverletzungen.

Folgendes Bsp läuft durch, obwohl es mMn eine Zugriffsverletzung geben müsste:

Code: Alles auswählen

type
 
  TMyThread = class(TThread)
  private
    procedure WriteInMemo;
  public
    aStr: String;
    procedure Execute; override;
  end;
 
...
 
procedure TForm1.Button1Click(Sender: TObject);
var
  aThread: TMyThread;
begin
  aThread:=TMyThread.Create(True);
  aThread.FreeOnTerminate:=True;
  aThread.aStr:='Test';
  aThread.Execute;      //lt. Wiki müsste nach Execute-Ende der Thread .terminate und .free aufgerufen werden
  aThread.Terminate;    //selbst dies führt zu keiner Zugriffsverletzung
//  aThread.Free;       //Nur wenn ich selber .Free aufrufe, kommt es in der nachfolgenden Zeile zur Zugriffsverletzung
 
  Sleep(100);
  aThread.Execute;
end;
 
{ TMyThread }
 
procedure TMyThread.WriteInMemo;
begin
  Form1.Memo1.Lines.Add(aStr);
end;
 
procedure TMyThread.Execute;
begin
  Synchronize(@WriteInMemo);
//  Terminate;          //Auch das führt zu keiner Fehlermeldung
end;
Ich hatte auch FreeOnTerminate im Constructor gesetzt mit unveränderten Ergebnis. Entweder geht die Option nicht oder der Pointer des Threads zeigt nach dem Terminate immer noch auf die gleiche Stelle im Speicher, sodaß das zweite "Execute" noch "gültig" ist?!

Zwei Fragen:
Habe ich einen Denkfehler und mache etwas falsch (wie ich einen Thread per Hand aufräume weiss ich, würde aber möglichst "FreeOnTerminate" nutzen)?
Ist das Verhaten nur bei mir (Win7 64bit, Lazarus 32bit Trunc und 1.2.4 gleiches Verhalten) so oder ist das unter anderen Betriebssystemen genauso?

Vielen Dank fürs miträtseln :)

Michael
Zuletzt geändert von Michl am So 23. Nov 2014, 07:53, insgesamt 3-mal geändert.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Thread wieder frei geben

Beitrag von Socke »

Während Synchronize() wartet der Thread bis der Hauptthread die Methode ausgeführt hat. Deswegen gibt es keine Zugriffsverletzung.

Der Ablauf ist in etwa:
  • Der Hauptthread startet TMyThread
  • TMyThread startet und wartet auf den Hauptthread
  • Der Hauptthread muss aber erst Button1Click beenden
  • Danach erst wird WriteInMemo ausgeführt
  • Jetzt wird der Thread freigeben
Du kannst nach dem Sleep(100) noch ein Application.ProcessMessages; einfügen; dann sollte eine Zugriffsverletzung auftreten (muss aber nicht)
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Thread wieder frei geben

Beitrag von Michl »

Danke Socke,

ist eine gute und für mich verständliche Erklärung, warum das in diesem Fall funktionieren könnte. Ich hatte mein ursprüngliches Projekt zuweit minimiert, dass der Mainthread nicht ordentlich abgearbeitet wird. Besser ist darum folgendes Bsp, was leider auch ohne Fehlermeldung funktioniert:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
begin
  aThread:=TMyThread.Create(True);
  aThread.FreeOnTerminate:=True;
  aThread.aStr:='Test';
  aThread.Execute;   
  aThread.Terminate;
//  aThread.Free; 
 
  aThread.Execute;
end;
 
procedure TForm1.Button2Click(Sender: TObject);
begin
  aThread.Execute;
end; 
Was mMn so nicht funktionieren dürfte!

Habe das Projekt nochmal entsprechend verändert, falls es jemand unter einem anderen OS testen wöllte?!

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

wp_xyz
Beiträge: 5174
Registriert: Fr 8. Apr 2011, 09:01

Re: Thread wieder frei geben

Beitrag von wp_xyz »

Mal ne ganz dumme Frage (bin nicht DER Thread-Spezialist...): Du erzeugst den Thread mit dem Parameter TRUE (CreateSuspended). Da ich nirgendwo ein "Thread.Start" oder "Thread.Resume" sehe, fängt der Thread gar nicht an zu laufen. Soll das so sein? Welchen Effekt hat das auf dein Problem?

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Thread wieder frei geben

Beitrag von Michl »

Bingo! Das wars! Im Button1Click läuft zwar der Thread mehrfach durch. Das hatte Socke ja entsprechend erklärt. Im Button2Click kommt es so aber zu einer Exception! Weiss gar nicht, warum ohne Start Execute funktioniert (wird evtl. dann als normale Methode durchlaufen?).

Vielen Dank!

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Thread wieder frei geben

Beitrag von Socke »

Code: Alles auswählen

type 
  TMyThread = class(TThread)
  private
    procedure WriteInMemo;
  public
    aStr: String;
    procedure Execute; override;
  end;
Die Methode Execute sollte niemals als public deklariert werden (sondern als protected); da sie den Code enthält, der im Thread ausgeführt wird, ist es unsinning, sie von Außen aufrufen zu wollen.

Damit lässt sich hier nämlich ein anderer Fehler vermeiden, den auch ich zuerst übersehen hatte:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
begin
  aThread:=TMyThread.Create(True);
  aThread.FreeOnTerminate:=True;
  aThread.aStr:='Test';
  aThread.Execute;   
  aThread.Terminate;
//  aThread.Free; 
 
  aThread.Execute;
end;
mit aThread.Execute springt der Hauptthread in die Methode Execute hinein. Diese wird dann ausgeführt -- vom Hauptthread nicht von TMyThread! Ein Thread wird über aThread.Start gestartet.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: [gelöst] Thread wieder frei geben

Beitrag von Michl »

Meistens starte ich Threads sofort. Hatte in letzter Zeit sehr viel mit TProcess gearbeitet. Irgendwie ist da wohl noch das .Execute hängen geblieben :wink:

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Antworten