问题
Application works as I'd like but there are quite big memory leakage. Every event that throttles one thread gives me 4 TBitmaps and 2 TStrokeBrush that are lost.
The procedure DrawSine(); is triggered in Execute in Synchronize statement:
procedure SineThread.DrawSine();
var
sin_T : Extended;
Point2 : TPoint;
I : Integer;
begin
TempBitmap.SetSize(Twidth, Theight);
TempBitmap.Canvas.BeginScene();
TempBitmap.Canvas.Stroke.Kind := TBrushKind.bkSolid;
TempBitmap.Canvas.Stroke.Color := claLime;
TempBitmap.Canvas.Clear(TAlphaColorRec.Black);
for I := 0 to Twidth do
begin
sin_T := Sin(((I - Tphas)/100.0) * Tfreq);
Point2.X := Round(I);
Point2.Y := Round(sin_T * Tampl) + Round(Theight/2.0);
if I = 0 then
begin
Point1.X := Round(I);
Point1.Y := Round(sin_T * Tampl) + Round(Theight/2.0);
TempBitmap.Canvas.DrawLine(Point1, Point2, 1.0, TempBrush);
end
else
begin
if I = Twidth then
begin
TempBitmap.Canvas.DrawLine(Point1, Point2, 1.0, TempBrush);
Point1.X := Round(I);
Point1.Y := Round(Theight/2.0);
end
else
begin
TempBitmap.Canvas.DrawLine(Point1, Point2, 1.0, TempBrush);
Point1.X := Point2.X;
Point1.Y := Point2.Y;
end;
end;
end;
TempBitmap.Canvas.EndScene();
end;
SineThread Constructor and Destructor:
constructor SineThread.Create(CreateSuspended: Boolean);
begin
inherited Create(CreateSuspended);
try
TempBitmap := TBitmap.Create();
TempBrush := TStrokeBrush.Create(TBrushKind.bkSolid, TAlphaColorRec.White);
finally
Twidth := 0;
Theight := 0;
Tampl := 0;
Tphas := 0;
Tfreq := 0;
Point1 := Point(0,0);
end;
end;
destructor SineThread.Destroy();
begin
inherited Destroy();
TempBitmap.Free();
TempBrush.Free();
end;
OnTerminate when finishing thread looks like:
procedure TForm1.OnTerminateProc1(Sender: TObject);
var
TempStream : TMemoryStream;
begin
try
TempStream := TMemoryStream.Create();
finally
(Sender as SineThread).GetBitmap.SaveToStream(TempStream);
Image1.Bitmap.LoadFromStream(TempStream);
TempStream.Free();
end;
end;
The Trigger() procedure is started every the the value on TrackBars change:
procedure TForm1.Trigger(Sender: TObject);
var
sine1_thread : SineThread;
sine2_thread : SineThread;
sineSum_thread : SineSumThread;
begin
try
begin
sine1_thread := SineThread.Create(True);
sine2_thread := SineThread.Create(True);
sineSum_thread := SineSumThread.Create(True);
end;
finally
begin
sine1_thread.SetSineParams(TrackBar1.Value, TrackBar2.Value, TrackBar3.Value);
sine1_thread.SetImageParams(Trunc(Image1.Width), Trunc(Image1.Height));
sine1_thread.FreeOnTerminate := True;
sine1_thread.OnTerminate := OnTerminateProc1;
sine1_thread.Start();
sine2_thread.SetSineParams(TrackBar4.Value, TrackBar5.Value, TrackBar6.Value);
sine2_thread.SetImageParams(Trunc(Image2.Width), Trunc(Image2.Height));
sine2_thread.FreeOnTerminate := True;
sine2_thread.OnTerminate := OnTerminateProc2;
sine2_thread.Start();
sineSum_thread.SetSineParams(TrackBar1.Value, TrackBar2.Value, TrackBar3.Value, TrackBar4.Value, TrackBar5.Value, TrackBar6.Value);
sineSum_thread.SetImageParams(Trunc(Image3.Width), Trunc(Image3.Height));
sineSum_thread.FreeOnTerminate := True;
sineSum_thread.OnTerminate := OnTerminateProc3;
sineSum_thread.Start();
end;
end;
end;
回答1:
It seems that the threads are not being destroyed. Since they are freed on terminate that seems odd. You set FreeOnTerminate
, so if the threads terminate they will be destroyed.
Let us assume that the threads to terminate. In which case the explanation is that your destructor is missing override directive. It should be declared like this:
destructor Destroy; override;
My psychic debugging skills (not infallible) tell me that you missed the override. So when Destroy
is called, the base class method runs and not yours.
The most effective way to track down leaks is to use the full version of FastMM. When configured correctly that will give stack traces for the allocation associated with a leak. And lots of other useful stuff to help find defects earlier.
Don't use finally
in a constructor's implementation. If an exception is raised then, the instance will be destroyed and so your finally
block is pointless.
Use the correct resource acquisition pattern:
obj := TMyClass.Create;
try
obj.Foo; // do stuff with obj
finally
obj.Free;
end;
As you write it, an exception raise in the constructor will lead to you calling Free
on an uninitialized instance variable.
Deallocate resource in reverse order to their acquisition. That means that your destructor should be written:
destructor SineThread.Destroy;
begin
TempBrush.Free;
TempBitmap.Free;
inherited;
end;
The finally
in TForm1.Trigger
is also wrong. A finally
block runs no matter what. If for some reason you fail to create an object, you must not carry on as if that failure did not happen. You use finally
to protect a resource. You acquire a resource, and use the finally
block to make sure that you release it no matter what.
There's absolutely no need for threads in your program. As you explained in your previous question, and mentioned again here, you use Synchronize
to put all the work onto the main threads. This renders the threads nugatory. I don't know why you chose to use threads. Perhaps you thought that by doing so, your program would perform better. That is not always the case, and certainly not when you implement threading the way you have done.
Programming is hard enough at the best of times, without needless complexity. Especially when you have not yet mastered the language. My advice is to do that first before moving on to advanced topics like threading.
Finally, you must learn to present complete, but cut-down examples for questions like this. You omitted quite a bit of code, and if I am right, the most important bit of code, that which causes the leak, was omitted.
回答2:
One general rule to remember is this one:
When an object's constructor raises an exception, it's destructor is called automatically.
So the try..finally
sequence in SineThread.Create
is not needed.
In an object's destructor, call inherited
as last item.
constructor SineThread.Create(CreateSuspended: Boolean);
begin
inherited Create(CreateSuspended);
TempBitmap := TBitmap.Create();
TempBrush := TStrokeBrush.Create(TBrushKind.bkSolid, TAlphaColorRec.White);
Twidth := 0;
Theight := 0;
Tampl := 0;
Tphas := 0;
Tfreq := 0;
Point1 := Point(0,0);
end;
destructor SineThread.Destroy();
begin
TempBitmap.Free();
TempBrush.Free();
inherited;
end;
same goes for OnTerminateProc1
:
procedure TForm1.OnTerminateProc1(Sender: TObject);
var
TempStream : TMemoryStream;
begin
TempStream := TMemoryStream.Create();
try
(Sender as SineThread).GetBitmap.SaveToStream(TempStream);
Image1.Bitmap.LoadFromStream(TempStream);
finally
TempStream.Free();
end;
end;
no need for try..finally
inTrigger()
:
procedure TForm1.Trigger(Sender: TObject);
var
sine1_thread : SineThread;
sine2_thread : SineThread;
sineSum_thread : SineSumThread;
begin
sine1_thread := SineThread.Create(True);
sine2_thread := SineThread.Create(True);
sineSum_thread := SineSumThread.Create(True);
sine1_thread.SetSineParams(TrackBar1.Value, TrackBar2.Value, TrackBar3.Value);
sine1_thread.SetImageParams(Trunc(Image1.Width), Trunc(Image1.Height));
sine1_thread.FreeOnTerminate := True;
sine1_thread.OnTerminate := OnTerminateProc1;
sine1_thread.Start();
sine2_thread.SetSineParams(TrackBar4.Value, TrackBar5.Value, TrackBar6.Value);
sine2_thread.SetImageParams(Trunc(Image2.Width), Trunc(Image2.Height));
sine2_thread.FreeOnTerminate := True;
sine2_thread.OnTerminate := OnTerminateProc2;
sine2_thread.Start();
sineSum_thread.SetSineParams(TrackBar1.Value, TrackBar2.Value, TrackBar3.Value, TrackBar4.Value, TrackBar5.Value, TrackBar6.Value);
sineSum_thread.SetImageParams(Trunc(Image3.Width), Trunc(Image3.Height));
sineSum_thread.FreeOnTerminate := True;
sineSum_thread.OnTerminate := OnTerminateProc3;
sineSum_thread.Start();
end;
来源:https://stackoverflow.com/questions/21601138/delphi-xe5-threads-and-memory-leak-when-operation-on-tbitmap