应用程序按照我的意愿工作但存在相当大的内存泄漏。 限制一个线程的每个事件都会给我4个TBitmaps和2个丢失的TStrokeBrush。
程序DrawSine();在Execute in Synchronize语句中触发:
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 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如下:
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;
每当TrackBars上的值发生变化时,都会启动Trigger()过程:
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;
答案 0 :(得分:5)
似乎线程没有被破坏。因为它们在终止时被释放,这似乎很奇怪。你设置FreeOnTerminate
,所以如果线程终止,它们将被销毁。
让我们假设要终止的线程。在这种情况下,解释是您的析构函数缺少覆盖指令。它应该像这样声明:
destructor Destroy; override;
我的通灵调试技巧(并非绝对可靠)告诉我你错过了覆盖。因此,当调用Destroy
时,基类方法运行而不是您的。
追踪泄漏的最有效方法是使用完整版的FastMM。正确配置后,将为与泄漏关联的分配提供堆栈跟踪。以及许多其他有用的东西,以帮助早期找到缺陷。
不要在构造函数的实现中使用finally
。如果引发异常,则实例将被销毁,因此finally
块无意义。
使用正确的资源获取模式:
obj := TMyClass.Create;
try
obj.Foo; // do stuff with obj
finally
obj.Free;
end;
在编写它时,构造函数中的异常引发将导致您对未初始化的实例变量调用Free
。
以与其获取相反的顺序释放资源。这意味着你的析构函数应该写成:
destructor SineThread.Destroy;
begin
TempBrush.Free;
TempBitmap.Free;
inherited;
end;
finally
中的TForm1.Trigger
也是错误的。无论如何,finally
块都会运行。如果由于某种原因您未能创建对象,则不得继续执行,就好像未发生该故障一样。您使用finally
来保护资源。您获取了一个资源,并使用finally
块来确保无论发生什么都释放它。
您的程序中绝对不需要线程。正如您在上一个问题中所解释的那样,并在此处再次提到,您可以使用Synchronize
将所有工作放在主线程上。这使得线程无效。我不知道你为什么选择使用线程。也许你认为通过这样做,你的程序会表现得更好。情况并非总是如此,当你按照自己的方式实现线程时肯定不会这样。
编程在最好的时候是很难的,没有不必要的复杂性。特别是当你还没有掌握这门语言的时候。我的建议是在继续进行线程等高级主题之前先做到这一点。
最后,您必须学会提供完整但有效的例子来解决这类问题。你省略了相当多的代码,如果我是对的,那么导致泄漏的最重要的代码就被省略了。
答案 1 :(得分:2)
要记住的一个一般规则是:
当对象的构造函数引发异常时,它的析构函数是 自动调用。
因此不需要try..finally
中的SineThread.Create
序列。
在对象的析构函数中,将inherited
作为最后一项调用。
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;
{p>同样适用于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;
try..finally
中不需要Trigger()
:
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;