Thread Resume

Alle Fragen zur Netzwerkkommunikation
Antworten
Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Thread Resume

Beitrag von Michl »

Hallo Allerseits,

Zumeist bin ich bisher mit den Threads klar gekommen, habe aber jetzt einen etwas anders gearteten Fall (Suche im Netz brachte mich bisher leider nicht wirklich weiter). Ich muss den Thread öfters starten, mit anderen Werten, dieses neue Starten bekomme ich nicht hin:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
begin
//  TestThread.Execute;
  TestThread.Resume;
end;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  TestThread:=TTestThread.Create(True);
end;
 
procedure TForm1.FormDestroy(Sender: TObject);
begin
  if not TestThread.Finished then begin
    TestThread.Terminate;
    TestThread.WaitFor;
  end;
end;
 
{ TTestThread }
 
procedure TTestThread.SendeCaption;
begin
  Form1.Caption:='Test ' + inttostr(random(100));
end;
 
procedure TTestThread.Execute;
begin
  Synchronize(@SendeCaption);
end;
 
constructor TTestThread.Create(CreateSuspended: boolean);
begin
  FreeOnTerminate := True;
  inherited Create(CreateSuspended);
end; 
Der Buttonklick soll den Thread neu starten, macht es aber nicht. Der Thread wird nur 1mal gestartet.

Schreibe ich statt Resume, Execute (lt. Wiki darf man das nicht), wird der Thread mehrmals gestartet, beim Form1.Destroy hängt sich das Prog bei WaitFor auf. Execute funktioniert aber nicht, wenn man z.B. CreateSuspended=False macht.

Wie ist das richtige Vorgehen?

Danke!

Michl

[Edit] warum das jetzt im Forum "Netzwerk" gelandet ist, weiss ich nicht, hatte eigentlich Freepascal gewählt (evtl. der Uhrzeit geschuldet :roll: ) So sorry for that...
Dateianhänge
TestThreads.zip
(125.44 KiB) 102-mal heruntergeladen

Code: Alles auswählen

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

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

Re: Thread Resume

Beitrag von Michl »

Habe mal

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
begin
  if not TestThread.Finished then begin
    TestThread.Terminate;
    TestThread.WaitFor;
  end;
  TestThread:=TTestThread.Create(False);
end; 
das funktioniert. Warum aber Resume nicht???

Code: Alles auswählen

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

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

Re: Thread Resume

Beitrag von theo »

Irgendwie kapier ich gerade nicht ganz, was du da machst.

Eins ist aber sicher: Wenn du FreeOnTerminate:=true hast, dann ist der Thread gewissermassen Free On Terminate, also weg.
Terminiert ist er, wenn es nichts mehr zu executen gibt, also bei dir mehr oder weniger sofort.

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

Re: Thread Resume

Beitrag von Michl »

Das ist mir schon soweit klar (spare mir halt das .free bei Form1.destroy), das Resume funktioniert aber auch nicht, wenn ich das weglasse...

"Gefreet" wird der Thread eigentlich nach dem Abarbeiten auch nicht wirklich, sonst könnte ich doch eigentlich nicht mit einer Threadmethode (TestThread.Finished) darauf zugreifen???

Code: Alles auswählen

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

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

Re: Thread Resume

Beitrag von Michl »

Ich hab das jetzt so umgestellt (zumindest schimpft weder HeapTrc, noch gibt es eine Exception bei Programmende)

Code: Alles auswählen

type
  TTestThread = class(TThread)
    private
      Zufall: Real;
      procedure SendeCaption;
    protected
      procedure Execute; override;
    public
      Constructor Create(CreateSuspended : boolean);
    end; 
...
procedure TForm1.Button1Click(Sender: TObject);
begin
  TestThread:=TTestThread.Create(False);
end;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  TestThread:=TTestThread.Create(False);
end;
 
procedure TForm1.FormDestroy(Sender: TObject);
begin
  if not TestThread.Finished then 
    TestThread.WaitFor;
end;
 
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Label1.Caption:=IntToStr(Random(100));
end;
 
{ TTestThread }
 
procedure TTestThread.SendeCaption;
begin
  Form1.Caption:='Durchschnittzufallszahl ' + FloatToStr(Zufall);
  Form1.Repaint;
end;
 
procedure TTestThread.Execute;
var
  i: Integer;
  r: real;
begin
  r:=0;
  for i:=0 to 100000000 do
    r += random(1000);
  Zufall:=r / 100000000;
 
  Synchronize(@SendeCaption);
end;
 
constructor TTestThread.Create(CreateSuspended: boolean);
begin
  FreeOnTerminate := True;
  inherited Create(CreateSuspended);
end;
Weiss aber nicht, ob das so richtig ist?!

Code: Alles auswählen

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

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2822
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Thread Resume

Beitrag von m.fuchs »

Aber was soll den fortgesetzt werden? Die Abarbeitung ist doch bereits abgeschlossen.

Kann es sein, dass du eigentlich so etwas machen möchtest?

Code: Alles auswählen

procedure TTestThread.Execute;
begin
  repeat
    Synchronize(@SendeCaption);
    Self.Suspend;
  until 1=2;
end; 
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

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

Re: Thread Resume

Beitrag von theo »

Michl hat geschrieben:Weiss aber nicht, ob das so richtig ist?!
Schon gut.
Für jeden Job einen neuen Thread machen und selber freen lassen ist mMn am sichersten und einfachsten, wenn man nichts anderes braucht.
Nicht unbedingt das Performanteste, aber passt schon.

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

Re: Thread Resume

Beitrag von Michl »

m.fuchs hat geschrieben:Aber was soll den fortgesetzt werden? Die Abarbeitung ist doch bereits abgeschlossen.

Kann es sein, dass du eigentlich so etwas machen möchtest?

Code: Alles auswählen

procedure TTestThread.Execute;
begin
  repeat
    Synchronize(@SendeCaption);
    Self.Suspend;
  until 1=2;
end; 
Dabei habe ich das Problem, dass ich beim Schließen nicht aus dem Thread rauskomme. Was mir nicht 100%ig klar war ist, dass Thread.Resume das Thread.Execute nicht von vorn aufruft, sondern nur das Suspend aufhebt - ist aber eigentlich logisch, wenn man mal ein bischen darüber nachdenkt :wink:
theo hat geschrieben:Schon gut.
Für jeden Job einen neuen Thread machen und selber freen lassen ist mMn am sichersten und einfachsten, wenn man nichts anderes braucht.
Nicht unbedingt das Performanteste, aber passt schon.
Danke, dann bin ich nicht auf dem Holzweg - da kann ich ja jetzt beruhigt schlafen gehen :)

Code: Alles auswählen

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

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 Resume

Beitrag von mschnell »

"Resume" lässt einen Thread weiterlaufen, den man vorher mit "Suspend" brutal mitten bei der Arbeit abgewürgt hat.

Ich finde die Suspend/Resume - Methodik wenig sinnvoll.

Statt "Resume" kannst Du den Thread auch auf Event warten lassen, wenn er die Arbeit getan hat, und das Event setzen damit er mit der nächsten Aufgabe wieder loslegt.

-Michael

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

Re: Thread Resume

Beitrag von Michl »

Meinst Du das so?!:

Code: Alles auswählen

procedure TTestThread.Execute;
...
begin
  while not Terminated do begin
    RtlEventWaitFor(ResumeEvent);
    if not Terminated then begin
...
end;
 
constructor TTestThread.Create(CreateSuspended: boolean);
begin
  inherited Create(CreateSuspended);
  ResumeEvent:=RTLEventCreate;
end;
 
destructor TTestThread.Destroy;
begin
  RTLEventdestroy(ResumeEvent);
  inherited Destroy;
end;
 
procedure TTestThread.Terminate;
begin
  RtlEventSetEvent(ResumeEvent);  //muss hier hin, sonst wartet er beim Schließen ewig auf das Event...
  inherited Terminate;
end;
 
procedure TTestThread.ResumeWork;
begin
  RtlEventSetEvent(ResumeEvent);
end;
 
probiert habe ich auch noch eine Boolsche Variable (z.B. FThreadStarten) und dann

Code: Alles auswählen

procedure TTestThread.Execute;
begin
  while not Terminated do begin
    if FThreadStarten then begin
      FThreadStarten:=False;
...
    end else Sleep(10);  
end;
 
procedure TTestThread.ResumeWork;
begin
  FThreadStarten:=True;
end;
Zweitere Variante ist übersichtlicher. Gehen tun beide. Weiss nicht was das bessere/allgemein günstigere Vorgehen ist?!

Verstehen tue ich, wenn man von außen Suspend (und nicht im Thread) aufruft, dass der Thread dann irgendwo abgewürgt wird, dass das in manchen Fällen nicht so günstig ist.

Code: Alles auswählen

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

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 Resume

Beitrag von mschnell »

Die zweite Variante macht ein "Busy Wait" verbrät also Rechenzeit auch wenn nichts zu tun ist. Die Rechenzeit fehlt dann den anderen Threads (und anderen Prozessen auf dem Rechner).

Das wird durch das sleep abgemildert, das sleep hat aber wiederum zur Folge, dass auf eine Anforderung ( FThreadStarten:=True; ) nicht sofort reagiert werden kann, sondern erst nach Ablauf der Sleep - Zeit (erhöhte Latenz).

"Waitfor" wartet mit Hilfe des Betriebssystems auf eine Semaphore, verbrät also keine Zeit, wenn nichts zu tun ist und reagiert (im Rahmen der Prioritäten) sofort auf eine Anforsderung.

Deshalb ist die erste Variante eindeutig besser.

"Suspend" ist meiner Ansicht nach "Thread Handling für Arme".

-Michael

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

Re: Thread Resume

Beitrag von Michl »

Wieder was gelernt :D :D :D

Viele Dank für die verständliche Erklärung!

Code: Alles auswählen

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

Antworten