[DELPHI 7] Timer in Thread probleem

Pagina: 1
Acties:
  • 254 views sinds 30-01-2008
  • Reageer

  • Geodan
  • Registratie: Maart 2003
  • Laatst online: 20-06 14:32
Ik heb een Thread gemaakt. Binnen deze thread wil ik een timer maklen die om de 5 seconde een actie uitvoert. Het aanmaken van de Timer gaat goed.
Met g_ThreadBinder.Log('beep',LogMessage); schrijf ik het weg naar een bestand/
De eerste beep komt er wel in, maar die in de functie TimerOnTimer niet.
Weet iemand hoe dit komt?

Gestripte versie van de code.

Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
unit ReadAeroScout;

interface

uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, SerialNG,StdCtrls,Grids,SyncObjs,Constants,ComObj,ReadThread,
    IdTCPClient,ScktComp,QExtCtrls,XMLDoc,xmldom,XMLIntf, msxmldom;

type
    TReadAeroScout = class(TReadThread)
        private
            HeartBeatTimer : TTimer;
            procedure TimerOnTimer(Sender: TObject);
            procedure SetName;
        protected
            procedure Execute; override;
    end;


var ReadThread : TReadAeroScout;

implementation

uses ThreadBinder;

{------------------------------- TReadThread -----------------------------------}
procedure TReadAeroScout.SetName;
var
    ThreadNameInfo : TThreadNameInfo;
begin
    ThreadNameInfo.FType := $100;
    ThreadNameInfo.FName := 'ReadThread';
    ThreadNameInfo.FThreadID := $AAAAAAAA;
    ThreadNameInfo.FFlags := 0;
    try
        RaiseException( $406D1388, 0, sizeof(ThreadNameInfo) div sizeof(LongWord),@ThreadNameInfo);
    except
    end;
end;

procedure TReadAeroScout.Execute;
begin
    SetName;
    g_ThreadBinder.Log('beep',LogMessage);      
    HeartBeatTimer := TTimer.Create(nil);
    HeartBeatTimer.OnTimer:=TimerOnTimer;
    HeartBeatTimer.Interval := 1000;
    HeartBeatTimer.Enabled := true;
end;


procedure TReadAeroScout.TimerOnTimer(Sender: TObject);
begin
    g_ThreadBinder.Log('beep',LogMessage);
end;

end.

  • NaliXL
  • Registratie: Maart 2002
  • Laatst online: 12-12 20:40
Sluit je je bestand wel goed? Anders kan je bij de 2e keer het bestand waarschijnlijk niet openen omdat het in gebruik is. In dit geval is code trouwens altijd wel handig.

Genoeg is meer dan veel, en tart den overvloed


  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 17-12 19:54

Creepy

Tactical Espionage Splatterer

Standaard terminate een thread zichzelf als de execute method van de thread is afgelopen. Zolang de execute method van een thread blijft lopen blijft de thread bestaan en dus de timer ook.

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Verwijderd

Een thread wordt beeindigd als de execute functie is uitgevoerd.

De simpelse oplossing is iets als een
code:
1
While (NotTerminated) do begin end
block in de thread te zetten maar eigenlijk is het een beetje onzin om een timer in een thread te gebruiken.
Door de verplichte While loop kun je ook "gewoon" vanuit de thread een sleep(1000) aanroepen.

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 13:03

Tomatoman

Fulltime prutser

Klopt het dat je probeert een thread te maken die 5 seconden niets doet, dan een actie uitvoert en vervolgens weer 5 seconden in slaap moet vallen tot de volgende actie? In dat geval kun je het beste de functie WaitForSingleObject gebruiken.

In je huidige code zit een bug: je creëert een TTimer, maar destroyt hem nergens --> geheugenlek.

Een goede grap mag vrienden kosten.


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Het onzin om een timer te maken in een thread, zoals Jan Klaasen ook al opmerkt. Maak zo'n loop in je Excute method:
Delphi:
1
2
3
4
5
6
7
8
procedure TReadAeroScout.Execute; 
begin 
  SetName; 
  while not Terminated do begin
    TimerOnTimer;
    Sleep(1000);
  end;
end; 

We adore chaos because we like to restore order - M.C. Escher


  • Geodan
  • Registratie: Maart 2003
  • Laatst online: 20-06 14:32
Dit is slechts een klein onderdeel van de code.
In deze thread wordt verbinding met een server gemaakt via TCP/IP

De server stuurt daarna berichten naar mijn programma.
Om de 5 seconde moet ik de server laten weten dat ik nog steeds berichten wil ontvangen.
Hiervoor heb ik dus de Heartbeat Timer gemaakt.De communicatie tussen client en server verloopt prima. Alleen na 5 seconde wordt de verbinding verbroken omdat de OnTimer functie van de Timer niet af gaat. Wanneer ik de Timer buiten deze thread aanmaak werkt het gewoon, maar in deze thread werkt het niet.

Is dit een bug? of is het algemeen bekend dat een TTimer object aangemaakt in een TThread niet werkt.

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 17-12 19:54

Creepy

Tactical Espionage Splatterer

Geodan schreef op maandag 13 december 2004 @ 11:34:
Is dit een bug? of is het algemeen bekend dat een TTimer object aangemaakt in een TThread niet werkt.
Heb je nu al geprobeerd de Execute method NIET te laten stoppen? Want als deze stopt dan killt de thread zichzelf.

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • Geodan
  • Registratie: Maart 2003
  • Laatst online: 20-06 14:32
Creepy schreef op maandag 13 december 2004 @ 11:50:
[...]

Heb je nu al geprobeerd de Execute method NIET te laten stoppen? Want als deze stopt dan killt de thread zichzelf.
Ja dat heb ik al een keer geprobeerd.
je kan de create ook met de property false aanroepen,
en dan freeOnTerminate dan stopt de thread ook niet gelijk

m_ReadThread := TThread.Create(false);
m_ReadThread.FreeOnTerminate := false;
m_ReadThread.Resume;

[ Voor 37% gewijzigd door Geodan op 13-12-2004 12:09 ]


Verwijderd

Geodan schreef op maandag 13 december 2004 @ 12:05:
[...]


Ja dat heb ik al een keer geprobeerd.
je kan de create ook met de property false aanroepen,
en dan freeOnTerminate dan stopt de thread ook niet gelijk

m_ReadThread := TThread.Create(false);
m_ReadThread.FreeOnTerminate := false;
m_ReadThread.Resume;
FreeOnTerminate zorgt er voor dat het TThread object vrijgegeven wordt, maar heeft niets te maken met de execute loop. Het nut van het niet vrijgeven van het object is dat je bepaalde public properties van de Thread van buiten de thread benaderd kunnen worden.
De parameter in de Create geeft aan of de thread gelijk moet starten of dat die in suspended state moet starten zodat je nog wat properties kan zetten en daarna de thread kan starten via Resume. Dus ook dit lost jou probleem niet op.

PS Dit staat gewoon in de handleiding.

  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Geodan schreef op maandag 13 december 2004 @ 11:34:
Is dit een bug? of is het algemeen bekend dat een TTimer object aangemaakt in een TThread niet werkt.
Een timer verstuurd windows messages die afgehaneld worden door de applicatie en dus de main thread. Het is dus alleen daaromal nutteloos om een timer in een thread te gebruiken. Bovendien is het onnodig en met de code die je wel hebt laten zien gewoon ook nog een bug aan jouw kant. Sloop die timer er gewoon uit en gebruik de code in de hier als oplossing gegeven wordt.

We adore chaos because we like to restore order - M.C. Escher


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 13:03

Tomatoman

Fulltime prutser

Om eventuele misverstanden te voorkomen: je thread bevat een bug. Zodra de thread wordt gestart, wordt de Execute method éénmaal doorlopen, dat is het fundamentele principe van een TThread descendant.

Nog afgezien van de vraag of je hele opzet zinnig is, wordt de Execute method hieronder in hooguit een paar milliseconden doorlopen, waarna je thread definitief klaar is.
Delphi:
1
2
3
4
5
6
7
8
9
procedure TReadAeroScout.Execute; 
begin 
    SetName; 
    g_ThreadBinder.Log('beep',LogMessage);         
    HeartBeatTimer := TTimer.Create(nil); 
    HeartBeatTimer.OnTimer:=TimerOnTimer; 
    HeartBeatTimer.Interval := 1000; 
    HeartBeatTimer.Enabled := true; 
end;
Wat je eigenlijk zou willen, is dit:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
procedure TReadAeroScout.Execute; 
begin 
    SetName; 
    g_ThreadBinder.Log('beep',LogMessage);         
    HeartBeatTimer := TTimer.Create(nil); 
    HeartBeatTimer.OnTimer:=TimerOnTimer; 
    HeartBeatTimer.Interval := 1000; 
    HeartBeatTimer.Enabled := true;
    while not Terminated do
      {controleer hier of de timer is afgegaan};
end;

Een goede grap mag vrienden kosten.


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

tomatoman schreef op dinsdag 14 december 2004 @ 18:56:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
procedure TReadAeroScout.Execute; 
begin 
    SetName; 
    g_ThreadBinder.Log('beep',LogMessage);         
    HeartBeatTimer := TTimer.Create(nil); 
    HeartBeatTimer.OnTimer:=TimerOnTimer; 
    HeartBeatTimer.Interval := 1000; 
    HeartBeatTimer.Enabled := true;
    while not Terminated do
      {controleer hier of de timer is afgegaan};
end;
Hmmm... heb jij hier geen Busy Waiting probleem gecreeerd?

  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Klopt Alarmnummer, daarom is het totaal onzinnig om een timer in een thread te gebruiken, zelfs als het al werkt en de TS bugvrije code weet te maken. Mijn oplossing doet hetzelfde zonder timer en zonder het Busy Waiting probleem.

[ Voor 7% gewijzigd door LordLarry op 14-12-2004 19:30 ]

We adore chaos because we like to restore order - M.C. Escher


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 13:03

Tomatoman

Fulltime prutser

Alarmnummer schreef op dinsdag 14 december 2004 @ 19:13:
Hmmm... heb jij hier geen Busy Waiting probleem gecreeerd?
Ik zal regel 10 in mijn code even uitschrijven:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
procedure TReadAeroScout.Execute;  
begin  
    SetName;  
    g_ThreadBinder.Log('beep',LogMessage);          
    HeartBeatTimer := TTimer.Create(nil);  
    HeartBeatTimer.OnTimer:=TimerOnTimer;  
    HeartBeatTimer.Interval := 1000;  
    HeartBeatTimer.Enabled := true; 
    while not Terminated do 
      if ProcessorWarmGenoeg then
        Sleep(1000); 
end;
:+

Een goede grap mag vrienden kosten.

Pagina: 1