2014-02-06 1 views
1

Приложение работает так, как хотелось бы, но есть довольно большая утечка памяти. Каждое событие, которое дросселирует один поток, дает мне 4 TBitmaps и 2 TStrokeBrush, которые потеряны.Delphi XE5 - Потоки и утечка памяти при работе на TBitmap

Процедура DrawSine(); запускается в Execute в заявлении Синхронизировать:

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 изменяется:

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

Ваш 'try ... finally' ошибочен ... Обычный шаблон =' MyObject: = TMyObject.Create() попробовать DoSomethingWith (MyObject), наконец, MyObject.Free; end; ' – whosrdaddy

ответ

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 тоже неправильно. A finally блок работает независимо от того, что. Если по какой-то причине вам не удается создать объект, вы не должны продолжать, как если бы этот сбой не произошел. Для защиты ресурса используется finally. Вы приобретаете ресурс и используете блок finally, чтобы убедиться, что вы его выпустили, несмотря ни на что.

В вашей программе абсолютно нет необходимости в потоках. Как вы объяснили в своем предыдущем вопросе и упомянули здесь еще раз, вы используете Synchronize, чтобы поместить всю работу в основные потоки. Это делает потоки неуязвимыми. Я не знаю, почему вы решили использовать потоки. Возможно, вы думали, что, сделав это, ваша программа будет работать лучше. Это не всегда так, и не обязательно, когда вы выполняете потоки так, как вы это делали.

Программирование достаточно сложно в лучшие времена, без лишней сложности. Особенно, если вы еще не освоили язык. Мой совет - сделать это сначала, прежде чем перейти к расширенным темам, таким как потоки.

Наконец, вы должны научиться представлять полные, но вырезанные примеры для таких вопросов. Вы опустили совсем немного кода, и если я прав, самый важный бит кода, который вызывает утечку, был опущен.

+0

психические навыки отладки !!! Я знал, что у меня что-то не хватает :) – whosrdaddy

+0

Я уже ответил на попытку ... наконец-то шаблон ??? – whosrdaddy

+0

@who Я знаю. Для ответа не больно. Как я мог опустить эту важную деталь. –

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; 

То же самое касается 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; 
+0

Спасибо, я еще не включил изменения в свой код, но ... У вас есть другой вопрос: существует ли разница, чтобы дать« унаследованный »как самый первый или последний оператор в унаследованном определении? Есть ли разница, если я даю «унаследованное + имя» или просто «унаследованное» (посмотрите теперь на свой код для Create and Destroy - они совсем разные)? –

+0

@Xs Почему нас спрашивают? Вы можете прочитать документацию. Для вас это откровенно грубо, чтобы вы этого не делали. –

+0

Стоит отметить, что ничто в этом ответе не объясняет ваши утечки. Звуковой совет здесь, но не решение проблемы утечки. –