27. juni 2007 - 14:25Der er
9 kommentarer og 1 løsning
Hjælp til at oprette en tråd
Jeg synes godt nok det her tråd halløj driller!
jeg har en ganske simpel tråd:
unit threadqueuesender;
interface
uses Classes, SysUtils;
type QSender = class(TThread) private { Private declarations } protected procedure Execute; override; public constructor CreateQSender; end;
implementation
{ QSender }
constructor QSender.CreateQSender; begin FreeOnTerminate := true; inherited Create(false); end;
procedure QSender.Execute; begin { Place thread code here } sleep(3000); end; end.
Og jeg har en knap på min form: if not assigned (QueueSender) then QueueSender := QSender.CreateQSender; else if QueueSender.Handle = 0 then QueueSender := QSender.CreateQSender;
Til at starte med kunne jeg nøjes med
if not assigned (QueueSender) then QueueSender := QSender.CreateQSender;
Men efter at have kompileret projektet nogle gang holdt den op med at nedlægge min tråd helt? Hvilket gjorde at der ikke skete noget anden gang jeg trykkede på knappen.
Min første tråd jeg prøvede med holdt til sidst helt op med at terminere selv om jeg endte med at fjerne det hele så den faktisk så ud som ovenstående...
Nå, men er min knap-måde helt hen i skoven eller hvordan checker jeg bedst på om min tråd er færdig og kan/skal oprettes igen?
Nu har jeg lige prøvet at flytte min funktionalitet over i den nye tråd så execute ser sådan her ud:
procedure QSender.Execute; begin { Place thread code here } try try //if not bWaitingForSmsReply then begin //select * from queue order by entrynmb asc limit 1 SQLContainer.qGetQueue.Active := false; SQLContainer.qGetQueue.Close; SQLContainer.qGetQueue.SQL.Clear; SQLContainer.qGetQueue.SQL.Add('select * from queue where problem=0 order by entrynmb asc limit 1'); SQLContainer.qGetQueue.Open; SQLContainer.qGetQueue.First; if (not SQLContainer.qGetQueue.Eof) then begin //if not eof then begin (* En masse kode *) end; //if not EOF except on E:exception do begin //add error in the log. //Synchronize(AddToLog); end; end; finally SQLContainer.qUpdateQueue.Close; SQLContainer.qGetQueue.Close; end; end;
Og nu laver den det nummer med ikke at terminate når den når til enden af execute. Tilbage i min hovedform at handle stadig 540 og threadid 3712 lige meget hvor længe jeg venter :( Hvorfor vil den ikke afslutte?
Hmm... tror jeg kom udenom problemet med at lave en tråd til at styre tråden med.
Nu er mit problem så at i min hovedtråd er jeg nødt til at bruge en sleep(); for ikke at den anden tråd skal stå og hamre databasen.
procedure SmsListen.Execute; var qlisten: qsender; sleeptimer: integer; begin { Place thread code here } repeat qlisten := qsender.CreateQSender; sleeptimer := qlisten.WaitFor; qlisten.Free; sleep(sleeptimer); until Terminated; end;
sleeptimer ændre størrelse alt efter hvad der foregår i køen. Men hvis køen er tom bliver den sat til 10 min. Problemet er hvis jeg kalder SmsListen.Terminate /.Free i mit hovedprogram kan der gå op til 10 min før objekten bliver nedlagt. Ikke hensigtsmæssigt at vente ~10 min på at ens program lukker ned fordi den venter på et objekt :) Er der en bedre måde at "time" en tråd på eller en måde at jeg kan tvinge tråden til at afslutte? Evt. med en boole af en art lige før den normalt ville gå til sleep så jeg ved om det er sikkert at tvinge den til at stoppe?
Tak for hjælpen Jesper
(den her tråd-del er sidste problem inden mit program er færdigt, så det er lidt frustrerende at det skal drille så meget :)
if not assigned(fQueueSender) then fQueueSender := TQSender.Create;
er nok. Grunden til du ikke får den oprettet flere gange er at QueueSender ikke bliver sat til nil når den stopper. Jeg ville oprette en liste af tråde og når en tråd besluttede sig til at dø, brugte den sin OnTerminate event til at fortælle din mainform at den havde besluttet sig for at dø. I eventet kunne du fiske adressen ud af listen:
fThreadList : TObjectList;
procedure TForm1.OnCreate; begin fThreadList := TObjectList.Create(false); // false = prøver ikke at frigive obj. end;
procedure TForm1.OnDestroy; var i : integer; begin for i := 0 to fThreadList.Count do (fThreadList[i] as TThread).Terminate;
fThreadList.Free; end;
procedure TForm1.OnButtonClick; var QSender : TQSender; begin if fThreadList.Count < 5 then // Max 5 tråde begin QSender := TQSender.Create; QSender.OnTerminate := OnThreadTerminate; fThreadList.Add(QSender); end; end;
procedure TForm1.OnThreadTerminate(aSender : TObject); begin fThreadList.Remove(aSender); end;
Hvad angår din "vent 10 minutter" kan du vel splitte den op i flere:
const TenMin = 600000; // ms. SleepPeriod = 250;
var i : integer; begin while not Terminated do begin i := 0; while not Terminated and (i < TenMin) do begin sleep(SleepPeriod); // Sov et halvt minut inc(i,SleepPeriod); end; end; end;
Ikke helt præcist 10 minutter, men skal du have det bedre kan GetTickCount bruges i stedet.
Selve ridset til en trådliste er ikke perfekt (klokken er mange). I dens OnDestroy, efter alle tråde var blevet bedt om at terminere, burde du Sleep'e i et loop indtil fThreadList.Count = 0. Derefter frigive listen.
Og ja, kommentaren "sov et halvt minut" er helt hen i vejret. Skulle stå et kvart sekund.
procedure TForm1.OnDestroy; const MaxSleeps = 10; var i, Loop : integer; begin for i := 0 to fThreadList.Count do (fThreadList[i] as TThread).Terminate;
Loop := 0; // Efter 2½ sekund gider jeg ikke vente længere while (Loop < MaxLoops) and (fThreadList.Count > 0) do begin Sleep(250); inc(Loop); end;
Mange godbidder her hrc... Trådlisten får jeg ikke lige brug for nu da jeg bare har en baggrundstråd til at køre der sørger for den anden tråd. Men den kan helt sikkert bruges på et andet tidspunkt. Havde ikke lige tænkt på det med FreeOnTerminate :) Og god idé med at dele tiden op på den måde.
Smid et svar, det var helt sikkert de 30 point værd. Så må du få nogle flere i det næste tråd-spørgsmål jeg opretter :D
Jeg var ikke færdig med at kommentere koden, men hva' skidt. Her er et svar.
procedure SmsListen.Execute; var QListen: TQSender; SleepTimer: integer; begin while not Terminated do begin QListen := TQSender.Create; try sleeptimer := qlisten.WaitFor; finally QListen.Free; end; sleep(sleeptimer); end; end;
P.S. Angiver du sleep(0) bliver tråden "sød" ved de andre processer, idet den fortæller operativsystemets dispatcher (er det ikke det rigtige navn?) at den giver afkald på sin timeslice til processorerne. Dem må andre få gode af. Medmindre der decideret er opslag til databaser eller andet hvor tråden gør andet end at vente, er sleep(0) god at bruge.
umiddelbart kan jeg ikke se hvornår der er mulighed for at sætte sleep til 0 da jeg enten skal vente 10 min eller 1 sek inden jeg skal køre tråden igen. Så jeg tror jeg bibeholder en blanding af dit sidste indlæg (skal selvfølgelig i try.. finally) og den tidligere med at dele sleeptimer op i flere bidder.
Tak for det hrc Jesper
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.