A
hi,
1. muss man ein mit new erzeugtes Objekt auch wieder mit delete zerstören.
2. Terminate() im Ereignis OnTerminate aufzurufen macht kein Sinn.
um dass mal zu untermauern hier mal die den wesentlichen Quellcode der Klasse TThread:
im Konstruktor wird mit BeginThread eine Thread erzeugt:
BeginThread(nil, @ThreadProc, Pointer(Self), FThreadID)
Die Procedure ThreadProc sieht dabei so aus:
function ThreadProc(Thread: TThread): Integer;
var
FreeThread: Boolean;
begin
// code, der davor kommt und für dieses Problem belanglos ist
try
if not Thread.Terminated then
try
Thread.Execute; // Dieses ist der eigentliche Thread.
except
Thread.FFatalException := AcquireExceptionObject;
end;
finally
FreeThread := Thread.FFreeOnTerminate;
Result := Thread.FReturnValue;
Thread.FFinished := True;
// an dieser Stelle ist bereits das wesentliche passiert. Der eigentliche Thread läuft noch, die Aufgaben des Thread
// welche in der Execute- Methde definiert wurden sind erledigt und der Thread wird nun gestopt.
// zuvor wird noch das Ereignis OnTerminate aufgerufen.
// Da der trhead noch läuft ruft DoTerminate das Ereignis OnTerminate Syncronisiert auf:
Thread.DoTerminate;
// wenn in dem Ereignis Terminate() aufgerufen wurde, interessiert es dem Thread recht wenig, da der Thread sowieso schon so gut wie tot ist.
// Wenn FreeOnTerminate gesetzt wurde, wird der Thread in der nächsten Zeile zerstört. Ansonsten existert der Thread weiter,
//sofern du den Thread nicht in OnTerminate zerstört hast, was immer ein Problem ist (Siehe unten)
if FreeThread then Thread.Free;
{$IFDEF MSWINDOWS}
EndThread(Result); // nun wird der Thread vom System abgemeldet
{$ENDIF}
// weiterer code
nun mal murphy's code:
void __fastcall TForm1::StartThreadButtonClick(TObject *Sender)
{
FNewThread = new TMyThread(false);
FNewThread->OnTerminate = DeleteThread;
}
wir gehen mal davon aus, dass FNewThread in der Header von TForm1 deklariert ist. Dann wird von TMyThread eine neue Instanz erzeugt. Diese wird aber nirgenswo zerstört. Selbst wenn du wolltest hast du immer ein Problem. Wenn du zum Beispiel schreibst:
void __fastcall TForm1::DeleteThread(TObject* Sender)
{
//Wenn dieser Code ausgeführt wird, ist die Threadberechnung zu ende.
delete FNewThread;
FNewThread=NULL;
}
funktioniert es, sofern nur eine Instanz gestartet wurde.
Was aber wenn der Thread 3 mal gestartet wurde, weil der User dreimal auf dem Button rumgetrampelt ist ?
dann ist folgendes passiert:
Es wurden 3 Instanzen erzeugt:
FNewThread = new TMyThread(false);
FNewThread = new TMyThread(false);
FNewThread = new TMyThread(false);
nun kommt das Ereignis On Terminate, weil die Erste Instanz fertig ist:
void __fastcall TForm1::DeleteThread(TObject* Sender)
{
//Wenn dieser Code ausgeführt wird, ist die Threadberechnung zu ende.
delete FNewThread;
FNewThread=NULL;
// So, hier ist nun FNewThread gelöscht.
}
jetzt ist die zweite Instanz fertig, und das Ereignis on Terminate wird aufgerufen:
void __fastcall TForm1::DeleteThread(TObject* Sender)
{
//Wenn dieser Code ausgeführt wird, ist die Threadberechnung zu ende.
delete FNewThread; // Fehler: FNewThread wurde bereits zerstört und ist NULL !
FNewThread=NULL;
}
die Folge sind Speicherlöcher und Zugriffsverletzungen ohne Ende.
Lösungsvorschlag ( ist vorzuziehen)
FreeOnTerminate auf true setzen, bevor der Thread gestartet wird.( siehe Quellcode von TThread oben )