Thread hängt beim Beenden

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Thread hängt beim Beenden

Beitrag von Scotty »

Mein Thread wird beim Beenden nicht immer richtig geschlossen. Was mache ich falsch?

Code: Alles auswählen

unit utimer;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, Forms, LMessages, LCLIntf;
 
const
  LM_THREADTIMER = LM_USER + 0;
 
type
 
  { TThreadTimer }
  TOnTimerTick=procedure(aElapsed:Longword) of Object;
 
  TThreadTimer = class(TThread)
  private
    FOnTimerTick : TOnTimerTick;
    FElapsed     : Longword;
    FInterval    : Longword;
    FPaused      : boolean;
    procedure SetElapsed(const AValue: Longword);
    procedure SyncMethod;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Execute; override;
    procedure Stop;
    property OnTimerTick:TOnTimerTick read FOnTimerTick write FOnTimerTick;
    property Pause:boolean write FPaused;
    property Interval:Longword read FInterval write FInterval;
    property Elapsed:Longword write SetElapsed;
  end;
 
implementation
 
{ TThreadTimer }
 
procedure TThreadTimer.SyncMethod;
begin
  if assigned(FOnTimerTick) then FOnTimerTick(FElapsed div 1000);
  if ((FElapsed div 1000)>=FInterval) then
  begin
    PostMessage(Application.Mainform.Handle, LM_THREADTIMER, 0, 0);
    FPaused:=true;
  end;
end;
 
procedure TThreadTimer.SetElapsed(const AValue: Longword);
begin
  FElapsed:=aValue*1000;
end;
 
procedure TThreadTimer.Stop;
begin
  OnTimerTick:=nil;
  Terminate;
  WaitForThreadTerminate(self.Handle,1000);
  //  FTimer.WaitFor; <- so muss gekillt werden
end;
 
constructor TThreadTimer.Create;
begin
  inherited Create(false);
  FreeOnTerminate:=true;
  Priority:=tpHigher;
  FPaused:=true;
end;
 
destructor TThreadTimer.Destroy;
begin
  inherited Destroy;
end;
 
procedure TThreadTimer.Execute;
var LastTickCount : Longword;
begin
  LastTickCount:=GetTickCount;
  while not Terminated do
  begin
    Sleep(100);
    if not Terminated and ((GetTickCount-LastTickCount)>1000) then
    begin
      if not FPaused then
      begin
        inc(FElapsed,(GetTickCount-LastTickCount));
        Synchronize(@SyncMethod);
      end;
      LastTickCount:=GetTickCount;
    end;
  end;
end;
 
end.

Benutzeravatar
theo
Beiträge: 10895
Registriert: Mo 11. Sep 2006, 19:01

Re: Thread hängt beim Beenden

Beitrag von theo »

Wozu ist das gut?
WaitForThreadTerminate(self.Handle,1000);

Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Re: Thread hängt beim Beenden

Beitrag von Scotty »

Das macht IMHO das gleiche wie WaitFor, wartet aber maximal 1000ms. Wenn ich WaitFor einsetze, hängt das Programm, so gibt es nur eine AV (warten muss ich, weil Sleep noch aktiv sein könnte). So weit ich das sehe, wird Sleep() oder GetTickCount() nach Application.Terminate nicht mehr sicher ausgeführt. Allerdings sehe ich dafür keinen Grund.
thread.inc hat geschrieben:function WaitForThreadTerminate (threadHandle : TThreadID; TimeoutMs : longint) : dword;
begin
Result:=CurrentTM.WaitForThreadTerminate(ThreadHandle,TimeOutMS);
end;

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Thread hängt beim Beenden

Beitrag von mschnell »

Scotty hat geschrieben:DSo weit ich das sehe, wird Sleep() oder GetTickCount() nach Application.Terminate nicht mehr sicher ausgeführt.
Du machst sleep() im Worker-Thread. Application läuft im Mainthread. Du darfst den Mainthread erst beenden, wenn der Worker-Thread beendet ist.

Deshalb ist TThread.WaitFor wohl schon richtig.

Der Thread kann sich aber nur selbst beenden. Er muss also aus dem sleep() und der Execure- Loop herauskommen, damit er das Terminate Flag setzen kann, das vermutlich in TThread.WaitFor abgefragt wird.

Der Mainthread hängt aber in TThread.WaitFor. Wenn der Thread ein TThread.Synchronize macht, braucht er den Mainthread um die synchonisierte Methode aufzurufen. Das tut er aber nicht, wenn er in TThread.WaitFor hängt. Dasselbe gilt für andere Mainthread-Events wie TTimer oder Procudure...Message.

-Michael

Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Re: Thread hängt beim Beenden

Beitrag von Scotty »

Du machst sleep() im Worker-Thread. Application läuft im Mainthread. Du darfst den Mainthread erst beenden, wenn der Worker-Thread beendet ist.
Das mache ich ja über Timer.Stop(), das nach OnClose durch eine Destroy-Routine aufgerufen wird.
Der Mainthread hängt aber in TThread.WaitFor. Wenn der Thread ein TThread.Synchronize macht, braucht er den Mainthread um die synchonisierte Methode aufzurufen. Das tut er aber nicht, wenn er in TThread.WaitFor hängt. Dasselbe gilt für andere Mainthread-Events wie TTimer oder Procudure...Message.
Heißt dass, ein einfaches "if not Application.Terminated" in der Synchronize-Methode würde helfen? Wenn ich mich recht erinnere, hatte ich das schon mal drin.

PS: Eben ausprobiert. So einfach geht es offenbar nicht.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Thread hängt beim Beenden

Beitrag von mschnell »

Nee. In der Synchronisierten Methode ist zu spät. Die läuft im Main-Thread, also gar nicht, wenn der im WaitFor hängt. Das "Synchonize" darf gar nicht erst ausgeführt werden. Application.terminated wird vermutlich auch zu spät gesetzt. Die Bedingung muss vor aufrufen von WaitFor gesetzt werden.

-Michael

Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Re: Thread hängt beim Beenden

Beitrag von Scotty »

Weder "not (csDestroying in Application.MainForm.ComponentState)" noch "not (AppDestroying in Application.Flags)" und "not Application.Terminated" funktionieren. Ich prüfe jetzt nur noch in der Execute-Methode und sehe, dass die nicht terminiert wird (ein write('x') hinter while...end kommt nicht). Synchronize() wird aufgerufen, da kein destroy-Flag gesetzt ist (if ... the write('+') else write('*')...).
Im Close der Mainform wird zuerst alles andere geschlossen und erst am Ende der Timer gestoppt. Mit größerem Aufwand könnte ich das ändern, aber eigentlich müsste doch das Flag stimmen.

Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Re: Thread hängt beim Beenden

Beitrag von Scotty »

Noch etwas Erfahrungsbericht: Da Sleep() angeblich böse ist, habe ich das durch RTLEventWaitFor() ersetzt. In beiden Fällen wird aber beim Beenden zuerst Execute abgearbeitet, bevor Terminate wirkt. D.h. auch wenn ich in einer Prozedur ein Flag setze, wird dieses noch nicht korrekt ausgewertet. Alle mir bekannten Destroyvariablen sind auch false. Jetzt habe ich keine Idee mehr, woher mein Thread Kenntnis darüber bekommen soll, dass die Anwendung geschlossen wurde.
Als workaround habe ich die Idlezeiten per RTLEventWaitFor(FSleep,10) auf 10ms minimiert und zähle bis 100. Dadurch gibt es während der Sleep-Phasen noch ein paar CPU-Ticks, die hoffentlich die Wahrscheinlichkeit erhöhen, dass Terminated erkannt wird.

Benutzeravatar
theo
Beiträge: 10895
Registriert: Mo 11. Sep 2006, 19:01

Re: Thread hängt beim Beenden

Beitrag von theo »

Was ist eigentlich genau dein Problem?
Muss denn der Main-Thread überhaupt noch da sein, wenn der Thread beendet wird?
Falls ja, warte doch einfach im Main-Thread, nachdem du das Terminated flags gesetzt hast. z.B. Sleep(1000);

Oder liege ich da falsch?

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Thread hängt beim Beenden

Beitrag von mschnell »

Scotty hat geschrieben:Da Sleep() angeblich böse ist,...
Warum soll sllep() böse sein ? Im Gegenteil: sleep ist sozial. Eine polling-schleife ohne sleep braucht 100 % der verfügbaren CPU. wenn Du sleep() einbaust lässt Du mehr Zeit für die anderen Prozesse und Threads. In Linux gibt sogar sleep(1) die Zeitscheibe auf und senkt die CPU-Verwendung drastisch.

polling _ohne_ sleep ist böse !

-Michael

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Thread hängt beim Beenden

Beitrag von mschnell »

Scotty hat geschrieben: Jetzt habe ich keine Idee mehr, woher mein Thread Kenntnis darüber bekommen soll, dass die Anwendung geschlossen wurde. .
Wenn ich mich recht erinnere: die normale Methode ist

im Main Thrfead:

Code: Alles auswählen

...
  myThread.Terminate;
  myThread.Waitfor;
  myThread.free;
im Thread execute.

Code: Alles auswählen

...
  while not Terminated do begin
    ...
  end;

nur das "..." muss natürlich immer zurück-kommen, ohne den Main thread z.B. durch Synchronize zu benötigen. Also

Code: Alles auswählen

...
  while not Terminated do begin
    ...
    if not Terminated Synchronize(SyncProc)
    ...
  end;
-Michael

Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Re: Thread hängt beim Beenden

Beitrag von Scotty »

RTLEventWaitFor() wartet genauso wie Sleep(). Egal, welches von beiden ich einsetze, Terminated ist beim ersten Vergleich immer false, wenn Terminate während des Sleep-Zyklus aufgerufen wird.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Thread hängt beim Beenden

Beitrag von mschnell »

Scotty hat geschrieben:RTLEventWaitFor() wartet genauso wie Sleep().
Die Funktionen machen etwas völlig anderes.

Sleep wartet eine bestimmte Zeit indem der ganze Thread durch einen Betriebssystem-Aufruf angehalten wird und erst nach dieser Zeit - plus der Zeit die bis zum Erreichen einer neuen Zeitscheibe vergeht - wieder aktiviert wird. Dabei wird keine Rechenzeit verbraten. sleep kann nicht auf ein bestimmtes Ereignis warten sondern nur eine bestimmte Mindest-Zeit.

WaitFor wartet auf ein bestimmtes Ereignis. Das kann die LCL/RTL auf verschiedene Weise implementioeren. Im simpelsten Fall als Busy-Loop (also "böse": 100 % CPU-Zeit) oder auch z.B. Message-getrieben durch procedure...message (dann geht es nur im Main-Thread).
Scotty hat geschrieben:Egal, welches von beiden ich einsetze, Terminated ist beim ersten Vergleich immer false, wenn Terminate während des Sleep-Zyklus aufgerufen wird.
Mach 'mal ein ganz simples Beispiel (ohne Synchronize und ähnliches) und schau was geht. Dann bau das ein, was Du brauchst, und schau was nicht geht.

-Michael

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Thread hängt beim Beenden

Beitrag von mschnell »

Scotty hat geschrieben:RTLEventWaitFor() wartet genauso wie Sleep().
Die Funktionen machen etwas völlig anderes.

Sleep wartet eine bestimmte Zeit indem der ganze Thread durch einen Betriebssystem-Aufruf angehalten wird und erst nach dieser Zeit - plus der Zeit die bis zum Erreichen einer neuen Zeitscheibe vergeht - wieder aktiviert wird. Dabei wird keine Rechenzeit verbraten. sleep kann nicht auf ein bestimmtes Ereignis warten sondern nur eine bestimmte Mindest-Zeit.

WaitFor wartet auf ein bestimmtes Ereignis. Das kann die LCL/RTL auf verschiedene Weise implementieren. Im simpelsten Fall als Busy-Loop (also "böse": 100 % CPU-Zeit) oder auch z.B. Message-getrieben durch procedure...message (dann geht es nur im Main-Thread).
Scotty hat geschrieben:Egal, welches von beiden ich einsetze, Terminated ist beim ersten Vergleich immer false, wenn Terminate während des Sleep-Zyklus aufgerufen wird.
Mach 'mal ein ganz simples Beispiel (ohne Synchronize und ähnliches) und schau was geht. Dann bau das ein, was Du brauchst, und schau was nicht geht.

-Michael

Antworten