![]() |
Delphi-Version: XE2
Thread nicht terminiert?
Hallo,
Umgebung: Rad Studio XE3 Update 2 Ich habe einen Thread (MThread), der während beim Click auf einen Button erzeugt wird. Mit Option freeonterminate=true Ein Ereignis onThreadTerminate wird zugewiesen. Nach Auslösen dieses Ereignisses wird einiges abgearbeitet - klappt auch alles soweit. Nur ein merkwürdiges Phänomen tritt auf: Wenn ich irgendwann später MThread.terminated abfrage, so ist dieser immer noch aktiv. - Es stört nicht wirklich. Auch ein erneutes Erzeugen und Starten klappt problemlos. Mache ich allerdings beim FormClose die Zeile: if assigned(MThread) then freeandnil(MThread) rein, knallt es. (Stört Euch nicht an der Formvariable oder so - ich arbeite hier mit einem multiple Docked-Forms Konzept, wo alle FRMs im Prinzip nur Muster sind und die Childforms dynamisch erzeugt werden - somit kann jede FRM mit eindeutigem Namen versehen werden und ich nutze da auch verschiedene Muster) Meine Problematik dreht sich nur um diesen MThread Hier mal ein wenig Code:
Delphi-Quellcode:
irgendwo - irgendwann muss der Thread ja "zerstört" werden?
type
TFrmGMaps = class(TChildform) WebGMaps1: TWebGMaps; starttimer: TTimer; . . . weitere Definitionen ... private { Private-Deklarationen } public { Public-Deklarationen } end; TMyThread = class(TThread) procedure execute; override; end; var threadaktiv : Boolean = False; MThread : TMyThread; // Form2 - Formvariable verworfen, wird bei Aufruf vergeben (Mutliple Forms) implementation {$R *.dfm} uses main, childformverwaltung, System.StrUtils, UDM; procedure TFrmGMaps.threadabschluss; var idx : integer; lat : string; lng : string; buwID : string; KName : string; begin if not threadaktiv then begin threadtimer.Enabled := false; pan_warten.Visible := false; setstartmarker; for idx := 0 to Length(threadkname)-1 do begin lat := threadlat[idx]; lng := threadlng[idx]; KName := threadKName[idx]; if ((lat<>'') and (lng<>'')) then WebGMaps1.Markers.Add(strtofloat(lat),strtofloat(lng), ID+' '+KName,geticon(99, False), True,True,True,False,false,idx); end; SetLength(threadlat,0); SetLength(threadlng,0); SetLength(threadkname,0); pan_gmapsteuerung.Visible := True; end; end; procedure TMyThread.Execute; var idx : integer; dynform : TFrmGMaps; // GETCUMLIST_DIST_FROM ist ne ORacle Stored Proc begin SetLength(threadlat,0); SetLength(threadlng,0); SetLength(threadkname,0); dynform := TFrmGMaps(Application.FindComponent(FrmMain.GmapThreadname)); dynform.GETCUMLIST_DIST_FROM.execute; if dynform.GETCUMLIST_DIST_FROM.ParamByName('RESULT').Asinteger=DM.pkgCodes.VariableByName('INF_SUCCESS').AsInteger then begin idx := 0; dynform.JvGradientProgressBar1.Max := dynform.GETCUMLIST_DIST_FROM.RecordCount; SetLength(threadlat,dynform.GETCUMLIST_DIST_FROM.RecordCount); SetLength(threadlng,dynform.GETCUMLIST_DIST_FROM.RecordCount); SetLength(threadkname,dynform.GETCUMLIST_DIST_FROM.RecordCount); while (not dynform.GETCUMLIST_DIST_FROM.eof) do begin Synchronize(dynform.JvGradientProgressBar1.StepIt); inc(idx); if not ((dynform.GETCUMLIST_DIST_FROMCUM_DBLGEOLAT.AsString = dynform.gmaps_lat) // Um auszuschließen, das der Startmarker and ( dynform.GETCUMLIST_DIST_FROMCUM_DBLGEOLNG.AsString = dynform.gmaps_lng)) // überschrieben wird then begin SetLength(threadlat,idx); SetLength(threadlng,idx); SetLength(threadkname,idx); threadlat[idx-1] := dynform.GETCUMLIST_DIST_FROMCUM_DBLGEOLAT.AsString; threadlng[idx-1] := dynform.GETCUMLIST_DIST_FROMCUM_DBLGEOLNG.AsString; threadkname[idx-1] := dynform.GETCUMLIST_DIST_FROMCUM_STRNAME.AsString; end; dynform.GETCUMLIST_DIST_FROM.Next; end; dynform.GETCUMLIST_DIST_FROM.Close; end else begin //Fehler end; end; procedure TFrmGMaps.btn_querystartClick(Sender: TObject); var lat : string; lng : string; KName : string; idx : integer; begin WebGMaps1.DeleteAllMapMarker; FrmMain.GmapThreadname := Self.Name; JvGradientProgressBar1.Position := 1; pan_warten.Left := Integer(Trunc((Self.Width - pan_warten.Width) / 2)); pan_warten.Top := Integer(Trunc((Self.Height - pan_warten.Height) / 2)); pan_warten.Visible := True; Application.ProcessMessages; MThread := TMyThread.Create(True); MThread.FreeOnTerminate := True; threadaktiv := true; MThread.OnTerminate := threadend; pan_gmapsteuerung.Visible := false; MThread.Resume; end; procedure TFrmGMaps.threadend(Sender: TObject); begin threadaktiv := false; threadabschluss; end; Gruss MC |
AW: Thread nicht terminiert?
Zitat:
Somit greift die Abfrage auf MThread.Terminated evtl. auf ein nicht mehr existierendes Objekt zu - Fehler! Und im FormClose ist MThread auch dann noch Assigned, wenn der Thread selber freigegeben ist, gleiches Problem! Vllt. solltest Du MThread im OnTerminated auf nil setzen, bzw. das Konzept weiter überdenken. |
AW: Thread nicht terminiert?
Zitat:
Die Procedure onterminate wird nur einmal aufgerufen. Das ist nicht das Problem. Möchte nur das der Thread gekillt wird. Wo kann ich das am Besten machen? Und womit? Freeandnil? Oder destroy? Gruss |
AW: Thread nicht terminiert?
.. wenn der Thread mit freeOnTerminate gestartet wurde
und im laufenden Thread ann die Execute Methode verlassen wird, dann ist er terminiert - nicht nil aber beendet. Das sollte auch im TaskManager verfolgbar sein. Grüße Klaus |
AW: Thread nicht terminiert?
Zitat:
im OnTerminate ein free... oder destroy? Gruss Jörg |
AW: Thread nicht terminiert?
Was willst Du denn noch "killen" bei FreeOnTerminate = True, wenn der Thread seine Execute-Methode beendet hat? Er ist dann terminiert und wird automagisch freigegeben.
|
AW: Thread nicht terminiert?
Du hast es anscheinend noch nicht verstanden. Der Thread ist terminiert. Nur das Threadobjekt selber ist nicht nil.
|
AW: Thread nicht terminiert?
Wenn Thread terminiert, threadaktiv auf false und MThread auf nil setzen. Vor einem Zugriff auf MThread dann eins dieser beiden im Vorfeld prüfen.
Delphi-Quellcode:
[edit] Siehe
if Assigned(MThread) then
MThread.MachIrgendwas; //oder if threadaktiv then MThread.MachIrgendwas; ![]() [edit2] Oh, Beitrags-Schnapszahl, ich geb virtuell einen aus :cheers: [/edit2] |
AW: Thread nicht terminiert?
Mal abgesehen davon, dass du globale Variablen benutzt, mit dem Thread in die VCL (nicht threadsafe!) reingreifst, deprecated Methoden benutzt (Resume) ...
Stell dir vor ich gebe dir meine Visitenkarte wo meine Adresse drauf steht. Lasse ich jetzt das Haus abreissen und du schaust danach dort vorbei, dann gibt es dort nichts mehr, auch wenn die Adresse auf der Visitenkarte mit goldenen Lettern geprägt und Parfüm beduftet wurde. Eine Objekt-Variable speichert nur die Referenz auf ein Objekt (also die Adresse) und wenn das Objekt nicht mehr da ist, dann hat die Objekt-Variable immer noch die Adresse auch wenn es dort nichts zu finden gibt. |
AW: Thread nicht terminiert?
jetzt hat´s geklingelt :wall:
Vielen Dank |
AW: Thread nicht terminiert?
Von hinten durch die Brust ins Auge:
Delphi-Quellcode:
Der Thread braucht offensichtlich ein TFrmGMaps-Objekt für seine Arbeit.
procedure TMyThread.Execute;
var dynform : TFrmGMaps; begin dynform := TFrmGMaps(Application.FindComponent(FrmMain.GmapThreadname)); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Dann wäre es sehr sinnvoll, wenn man dem Thread dieses Objekt von Aussen geben würde. Dadurch, dass der Thread sich diese Info selbst besorgen muss, entsteht ein Kuddelmuddel. Der Thread braucht und soll die Objekte "Application" und "FrmMain" nicht kennen.
Delphi-Quellcode:
TMyThread = class(TThread)
public dynform : TFrmGMaps; // NEU <==== procedure execute; override; end; procedure TFrmGMaps.btn_querystartClick(Sender: TObject); ... begin ... // und Erzeugen sieht dann so aus MThread := TMyThread.Create(True); MThread.FreeOnTerminate := True; MThread.OnTerminate := threadend; MThread.dynform := self; // <=== |
AW: Thread nicht terminiert?
Zitat:
Mit einer globalen Var und dann: dynform := TFrmGMaps(Application.findcomponent(threadname)); geht es ... Warum nicht Application. x..... ? Gruss MC |
AW: Thread nicht terminiert?
Zitat:
Dafür gibt es mehrere Gründe; die vollständige Liste kann ich leider nur auf Englisch anbieten: ![]() Globale Variablen sind sozusagen das Gegenteil von Objekt-orientierter-Programmierung bei der man versucht, alle Aspekte möglichst lokal zu behandeln. (Stichwort: ![]() Zitat:
Delphi-Quellcode:
Hier noch das Grundprinzip:
TMyThread = class(TThread)
public dynform : TFrmGMaps; // <== das fehlt bei dir procedure execute; override; end; Bevor ein Thread gestartet wird, gibt man ihm von Aussen alle Informationen, die er braucht. Der Thread kann sich dann ganz auf seine Aufgabe konzentrieren; er braucht nicht Application.FindComponent aufrufen, weil er sämliche Infos (Objekte, Daten, Werte) schon beim Start bekommen hat. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:01 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz