Зависает форма при работе потока по рисовании на Image после тысяч итераций.

Андрей Игоревич
Дата: 02.07.2019 15:12:34
В общем есть поток, в котором готовится и потом через Synchronize рисует изображение.
Пока учусь и тренируюсь такие вещи рисовать,потому изображение тестовое. Вроде всё хорошо рисует, обновляет и прочее, но после значительного количества итераций (много тысяч) программа зависает (not response). При том может зависнуть на 1000 итерации, а может пол часа нормально крутить и докрутить до 50000 не зависнув. Никаких сообщений о памяти, ресурсах, доступе, и прочем нет. Зависает скорее через время, нежели чем от числа повторений, если sleep поставить меньше, то и зависнет при меньшем числе итераций, но время зависания тоже разное, иногда почти сразу, иногда очень долго.

В потоке к форме не обращаюсь нигде, кроме ShowResult; (ну очень старался, вроде всё проверил)

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

+

TDravRandom = class(TThread)
  private
    { Private declarations }
    MyImage: TImage;
  protected
    procedure Execute; override;
    procedure ShowResult; //
end;


procedure TDravRandom.Execute;
var

  Iter:Integer;
  i,X,Y:Integer;
  Rr,delt,skl:Real;
  x0,y0:integer;
  share_r,ratio:real;
  r:integer;

begin

  DrawingRandom:=True;
 // while (DrawingFile=-1) and (not Terminated)  do
  MyImage:=TImage.Create(nil);
  MyImage.Width:=CoreShell.Image1.Width;
  MyImage.Height:=CoreShell.Image1.Height;
  Iter:=0;

while (DrawingFile=-1) and (not Terminated)  do
    begin

      MyImage.Width:=CoreShell.Image1.Width;
      MyImage.Height:=CoreShell.Image1.Height;

      MyImage.Canvas.Lock;

    Rr:=1e10;
    for i:=2 to InitialData.fa_location.NVig do     //для масштабирования картинки
      begin
       delt:=Sqrt(((InitialData.fa_location.TVSCord[i].x-InitialData.fa_location.TVSCord[1].x)*(InitialData.fa_location.TVSCord[i].x-InitialData.fa_location.TVSCord[1].x))+
                 ((InitialData.fa_location.TVSCord[i].y-InitialData.fa_location.TVSCord[1].y)*(InitialData.fa_location.TVSCord[i].y-InitialData.fa_location.TVSCord[1].y)));
       if delt<Rr then Rr:=delt/2;
       end;

      skl:=3700/MyImage.Height;    //масштаб
      r:=Round(1130*Rr/(skl)*0.9);       //радиус шестигранника
      ratio:=r/GlTuningImage.ReloadTuning.ratio;

       MyImage.Canvas.Pen.Color:=clBlack;
       MyImage.Canvas.Font.Size:=Round(GlTuningImage.ReloadTuning.text[1].frontsize*2*ratio);

      x0:=Round(1*(MyImage.Width)/2); //центр
      y0:=Round(1*(MyImage.Height)/2);

      Inc(iter);
      MyImage.Canvas.TextOut  (10,10,inttostr(Iter)); //вывожу итерации просто чтоб понять когда зависло

      Draw_random_core_in_thread (MyImage,x0,y0,r,Rr,skl,ratio);

MyImage.Canvas.UnLock;

    //  if CoreShell.TreeView1.Selected.Index<>0 then exit;
      Synchronize(ShowResult);
      MyImage.Picture:=nil;
      Sleep(10); //так часто чтоб быстрее зависло, иначе часами ждать можно
    end;
    
  DrawingRandom:=false;
end;

procedure TDravRandom.ShowResult;
begin
   CoreShell.Image1.Picture:=MyImage.Picture;
end;


Ну и вызываемые процедурки:

procedure Draw_random_core_in_thread (TempImage:TImage;x0,y0,r:integer; Rr,skl,ratio:Real);
var
  i,X,Y:Integer;
  delt:Real;
//  x0,y0:integer;

 // r:integer;
begin
  for i:=1 to InitialData.fa_location.NTVS do//
    begin

       X:=Round(x0+1000*InitialData.fa_location.TVSCord[i].x/skl);
       Y:=Round(y0-1000*InitialData.fa_location.TVSCord[i].y/skl);

       TempImage.Canvas.Brush.Color:=RGB(Random(100)+150, Random(100)+150, Random(100)+150);
       DrawPolygon (r,X,Y,TempImage.Canvas);
       TempImage.Canvas.Brush.Style := bsClear;
       TempImage.Canvas.TextOut( Round(X-TempImage.Canvas.TextWidth(inttostr(i))/2+GlTuningImage.ReloadTuning.text[1].Location.X*ratio),
                                        Round(Y+GlTuningImage.ReloadTuning.text[1].Location.Y*ratio),
                                        inttostr(i));  
     end;
end;



procedure DrawPolygon (r,x0,y0:Integer;Sender: TObject);     //рисуем многоугольник радиуса Р в точке Х0У0
var
a:array[1..6] of TPoint;
i:Integer;
begin
for i:=1 to 6 do
  begin
   a[i].X:=Round(r*cos(i*2*pi/6+pi/2)+x0);
   a[i].Y:=Round(r*sin(i*2*pi/6+pi/2)+y0);
  end;
(Sender as Tcanvas).Polygon(a);
end;


Картинка с другого сайта.
_Vasilisk_
Дата: 02.07.2019 15:33:43
Андрей Игоревич
В потоке к форме не обращаюсь нигде, кроме ShowResult
Андрей Игоревич
  MyImage.Width:=CoreShell.Image1.Width;
  MyImage.Height:=CoreShell.Image1.Height;
  Iter:=0;

while (DrawingFile=-1) and (not Terminated)  do
    begin

      MyImage.Width:=CoreShell.Image1.Width;
      MyImage.Height:=CoreShell.Image1.Height;

Далее, TImage - это класс VCL для отрисовки изображения. Для работы в потоке не предназначен. Вам нужен TBitmap
_Vasilisk_
Дата: 02.07.2019 15:35:46
Про дофигища глобальных переменных я молчу
Андрей Игоревич
Дата: 02.07.2019 16:01:37
_Vasilisk_
Андрей Игоревич
В потоке к форме не обращаюсь нигде, кроме ShowResult
Андрей Игоревич
  MyImage.Width:=CoreShell.Image1.Width;
  MyImage.Height:=CoreShell.Image1.Height;
  Iter:=0;

while (DrawingFile=-1) and (not Terminated)  do
    begin

      MyImage.Width:=CoreShell.Image1.Width;
      MyImage.Height:=CoreShell.Image1.Height;

Далее, TImage - это класс VCL для отрисовки изображения. Для работы в потоке не предназначен. Вам нужен TBitmap

Но в данном случае я же просто спрашиваю размер, никак не изменяя, он мне нужен при каждой итерации (если пользователь растянул или уменьшил окно). Да и этот код я вставил уже позже, когда зависания уже были.
А чем плох запрос значения глобальной переменной без изменения её значения (далее я хотел бы рисовать куда более сложные изображения с сотнями параметров, вводить (а затем ещё и постоянно обновлять) их в поток через указатели или ещё как было бы ну очень проблемно).

TBitmap сейчас опробую, может ли быть в этом причина?
Квейд
Дата: 02.07.2019 16:04:19
Андрей Игоревич
_Vasilisk_
пропущено...
пропущено...

Далее, TImage - это класс VCL для отрисовки изображения. Для работы в потоке не предназначен. Вам нужен TBitmap

Но в данном случае я же просто спрашиваю размер, никак не изменяя, он мне нужен при каждой итерации (если пользователь растянул или уменьшил окно). Да и этот код я вставил уже позже, когда зависания уже были.
А чем плох запрос значения глобальной переменной без изменения её значения (далее я хотел бы рисовать куда более сложные изображения с сотнями параметров, вводить (а затем ещё и постоянно обновлять) их в поток через указатели или ещё как было бы ну очень проблемно).

TBitmap сейчас опробую, может ли быть в этом причина?


Не забывай про Canvas.Lock / Unlock
_Vasilisk_
Дата: 02.07.2019 16:26:45
Андрей Игоревич
Но в данном случае я же просто спрашиваю размер, никак не изменяя
Если слово нельзя для вас не аргумент, то почитайте Рихтера. У него очень хорошо расписана работа с разными потоками
Андрей Игоревич
А чем плох запрос значения глобальной переменной без изменения её значения
Тем, что это значение внезапно может измениться
Андрей Игоревич
их в поток через указатели
Читайте Рихтера
Андрей Игоревич
ещё как было бы ну очень проблемно
Бросайте вы это занятие. Программирование это одна сплошная проблема
white_nigger
Дата: 02.07.2019 16:42:20
Ни TBitmap, ни TCanvas не стоит использовать в потоках. ВСЯ отрисовка с созданием необходимых классов - должна быть в основном потоке. И нет необходимости пересоздавать на каждый чих TImage. В потоке только подготовка данных. Если уж очень хочется что-то поместить в поток - то только голый GDI или GDI+
Андрей Игоревич
Дата: 02.07.2019 16:54:09
_Vasilisk_
Андрей Игоревич
Но в данном случае я же просто спрашиваю размер, никак не изменяя
Если слово нельзя для вас не аргумент, то почитайте Рихтера. У него очень хорошо расписана работа с разными потоками
Андрей Игоревич
А чем плох запрос значения глобальной переменной без изменения её значения
Тем, что это значение внезапно может измениться
Андрей Игоревич
их в поток через указатели
Читайте Рихтера
Андрей Игоревич
ещё как было бы ну очень проблемно
Бросайте вы это занятие. Программирование это одна сплошная проблема

Вот вроде и ответили, а вроде и зачем это написали? К чему?
Меня устраивает изменение глобальных переменных на ходу, если я понимаю где и как это происходит. Возможно сделаю иначе. Но расчеты трехмерных полей и объектов вещь специфическая (колоссальные объемы данных и параметров) и часто я понимаю, что сделать так как обычно делают становиться невозможно ли запредельно сложно.


Вот лучше скажите, как очистить BitMap чтоб не происходило "переполнение ресурсов". Если MyImage.Picture:=nil; с этим справлялось, то, например MyBitMap.FreeImage - нет. Можно, конечно в каждом цикле создавать (Create) и освобождать(Free), что в принципе прекрасно работает - но выглядит это как-то неправильно.
Dimitry Sibiryakov
Дата: 02.07.2019 17:01:49

white_nigger
Ни TBitmap, ни TCanvas не стоит использовать в потоках.

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

Posted via ActualForum NNTP Server 1.5

_Vasilisk_
Дата: 02.07.2019 17:03:31
Андрей Игоревич
К чему?
К тому, чтобы вы понимали, что делаете неправильно.
Андрей Игоревич
Меня устраивает изменение глобальных переменных на ходу, если я понимаю где и как это происходит
Никогда не смотрел на сигналы светофора. Меня устраивает, если я просто перебегаю дорогу