Отображение информациии во время выполнения цикла

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Отображение информациии во время выполнения цикла

Сообщение vovavova » 31.10.2019 12:50:41

День добрый. Подскажите, пожалуйста, сделать, что-бы во время выполнения цикла, на каждой итерации промежуточные значения переменных отображались на Label? Добавляю их в Мемо - все ок (добавляются по мере выполнения цикла), а в Label показывает только последнее значение в конце выполнения. Пробовал через таймер читать из глобалки - тоже самое. Не могу понять, как сделать...
vovavova
незнакомец
 
Сообщения: 1
Зарегистрирован: 31.10.2019 12:19:30

Re: Отображение информациии во время выполнения цикла

Сообщение Снег Север » 31.10.2019 22:04:41

Код: Выделить всё
for i := 0 to 100 do
begin
       label1.caption := IntToStr(i);
       Application.ProcessMessages;
end;


учтите, что на современном быстром компе цикл пролетает за миллисекунды, так что промежуточных значений вы, скорее всего, не увидите всё равно
Аватара пользователя
Снег Север
долгожитель
 
Сообщения: 2233
Зарегистрирован: 27.11.2007 16:14:47

Re: Отображение информациии во время выполнения цикла

Сообщение скалогрыз » 31.10.2019 22:39:58

ускорим цикл чуток
Код: Выделить всё
var
  t : qword;
begin
  ...
  t:=GetTickCount64;
  for i := 0 to 100 do
  begin
    label1.caption := IntToStr(i);
    if GetTickCount64 - t > 50 then begin
      Application.ProcessMessages;
      t:=GetTickCount64;
    end;
  end;

Делаем application process messages только каждые 50 мс.

на каждой итерации промежуточные значения переменных отображались на Label? Добавляю их в Мемо - все ок (добавляются по мере выполнения цикла)

в вообще делай так:
Код: Выделить всё
label1.Text := label1.Text +  InttoStr(i)+';'

все промежуточные будут видны
скалогрыз
долгожитель
 
Сообщения: 1694
Зарегистрирован: 03.09.2008 02:36:48

Re: Отображение информациии во время выполнения цикла

Сообщение zoltanleo » 01.11.2019 09:46:55

скалогрыз писал(а):ускорим цикл чуток

Дим, по феншую итерацию вообще надо бы в доп.поток обернуть, а рисовать в основном. Вдруг чел пишет сложные вычисления для атомной электростанции? :)

Код: Выделить всё
procedure TForm1.Button1Click(Sender: TObject);
var
  MyThr: TMyThr;
begin
  Button1.Enabled:= False;

  MyThr:= TMyThr.Create(True);
  try
    while not MyThr.Finished do
    begin
      Application.ProcessMessages;
    end;
  finally
    FreeAndNil(MyThr);
    Button1.Enabled:= True;
  end;

end;

{ TMyThr }

procedure TMyThr.ShowCounter;
begin
  Form1.Label1.Caption:= Format('Текущее значение счетчика: %d',[FCnt]);
end;

procedure TMyThr.Execute;
begin
  while FCnt < 100 do
  begin
    FCnt:= FCnt + 1;
    Synchronize(@ShowCounter);
    Sleep(50);
  end;

end;

constructor TMyThr.Create(CreateSuspended: Boolean);
begin
  Inherited Create(CreateSuspended);
  FreeOnTerminate:= False;
  Priority:= tpLower;
  FCnt:= 0;

  if CreateSuspended then Start;
end;                                 
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Аватара пользователя
zoltanleo
постоялец
 
Сообщения: 332
Зарегистрирован: 17.10.2013 10:55:01

Re: Отображение информациии во время выполнения цикла

Сообщение serbod » 02.11.2019 21:29:38

Совет автора софта в том числе для атомной электростанции - не использовать Application.ProcessMessages().

Дело даже не в том, что нарушается порядок выполнения кода и обработки исключений, растет стек и возможно вхождение в рекурсию с переполнением стека. И еще куча неочевидных проблем с блокировками и хендлами. Основная проблема, что программа будет работать в тысячу раз медленней. Если вас это устроит, то пожалуйста.

Правильное решение уже озвучили - выполнять вычисления не в основном потоке приложения, а в отдельном потоке. А в основном по таймеру каждые 100 миллисекунд (10 раз в секунду) обновлять отображаемое значение прогресса.

Если производительность не важна, но нужно видеть каждый шаг цикла (исследование алгоритмов, например) то это лучше сделать без цикла, в виде отдельной процедуры, которую нужно несколько раз запускать. И запускать нажатием кнопки и/или по таймеру.
Аватара пользователя
serbod
постоялец
 
Сообщения: 433
Зарегистрирован: 16.09.2016 11:03:02
Откуда: Минск

Re: Отображение информациии во время выполнения цикла

Сообщение sts » 03.11.2019 02:10:18

кстати как там дела в лазарусе с выделением ГУИ в отдельный поток? есть какоето штаnное решение?
sts
постоялец
 
Сообщения: 272
Зарегистрирован: 04.04.2008 12:15:44
Откуда: Тольятти

Re: Отображение информациии во время выполнения цикла

Сообщение Снег Север » 03.11.2019 12:04:42

serbod, вы в принципе правы, но такие проблемы актуальны, разве что, для атомных электростанций... :D
Топикстартер же, судя по самому вопросу, весьма далек от проектов подобной сложности.
Аватара пользователя
Снег Север
долгожитель
 
Сообщения: 2233
Зарегистрирован: 27.11.2007 16:14:47

Re: Отображение информациии во время выполнения цикла

Сообщение olegy123 » 03.11.2019 17:45:09

Application.ProcessMessages;
Нужен для принудительного "пинания" стэка сообщений.. В винде все оконные взаимодействия осущетвляются путем засылки TMessage.
Application.ProcessMessages; - пинает главный проток на прочтения этих сообщений.

Application.ProcessMessages; - для линивых и новичков и рукожопых..

TThread и в нем Synchronize; - это способ правильно вмешатся в главный поток, когда он закончил работу и впал в Idle, попробовать в этот момент поработать за него.. например переменные поменять..
Такой же подход применяется в Android, там вообще запрещено сейчас даже работать со http без отдельного потока и без вызова типа runOnUiThread. Главному потоку ничего не должно мешать во время работы, чтобы не словить ошибку или не заморозить (frezze) аппликуху..

TTimer.OnTimer - очень схожий с
Код: Выделить всё
procedure TMyThr.Execute;
begin
  while FCnt < 100 do
  begin
    FCnt:= FCnt + 1;
    Synchronize(@ShowCounter);
    Sleep(50);
  end;

end;
только он более простой и правильный.. так как работает вместе главным потоком..
Главный поток буде обрабатывать TTimer.OnTimer в очереди сообщений..

TTimer более подходит. Но при жестком "реальном" времени правильнее использовать TThread + Synchronize возможны мютексы, сигналы, защищеные переменные.. и т.п.
olegy123
энтузиаст
 
Сообщения: 1496
Зарегистрирован: 25.02.2016 12:10:20

Re: Отображение информациии во время выполнения цикла

Сообщение zoltanleo » 03.11.2019 20:57:32

olegy123 писал(а):Application.ProcessMessages; - для линивых и новичков и рукожопых..

Хорошо сказал. Попробуй в моем коде выше убрать ProcessMessages из главного потока.

зы. Не люблю я TTimer, не помню точно почему, но не люблю :D
Аватара пользователя
zoltanleo
постоялец
 
Сообщения: 332
Зарегистрирован: 17.10.2013 10:55:01

Re: Отображение информациии во время выполнения цикла

Сообщение olegy123 » 04.11.2019 05:15:47

Предназначение главного потока - это работа с оконными сообщениями в системе windows и linux: перерисовать окно, изменить размеры, произошел клик мышкой и т.п.
Frizze апликейшен происходит тогда когда главный поток заблудился в написанном неправильно коде, потождать загрузки с сайта, выполнить миллиардный подсчет в цикле.. Ему некогда разобрать сообщения от windows.

zoltanleo писал(а):Попробуй в моем коде выше убрать ProcessMessages из главного потока.

Цель, если нужно независимо от главного потока(т.е. без внутренного подсчета им, а это "морозит" окно)..
То нужно сделать параллельное работу. TTimer удобен тем что не создает проблемы в коде если нужно "распаралелить" в рамках главного потока задачи, TTimer посылает в очередь сообщение о необходимости обработать OnTimer процедуру.. Так что это не совсем параллельное вычисление.
Тут важно чтобы в OnTimer небыло тяжелой работы для главного потока, потому что это может также заморозить окно.

Для тяжелой работы предназначен TThread, но он работает на уровне операционной системы и CPU, которые не знают о существовании главного потока. Synchronize это способ правильно разрулить и развести когда есть необходимость влезть в сферу главного потока. Принудительно вызывать в нем Application.ProcessMessages - это неправильно.
olegy123
энтузиаст
 
Сообщения: 1496
Зарегистрирован: 25.02.2016 12:10:20

Re: Отображение информациии во время выполнения цикла

Сообщение Снег Север » 04.11.2019 08:45:31

Дополнительные потоки точно так же занимают память и процессор, как и основной процесс. Поэтому единственная польза от них - работа в "режиме Юлия Цезаря" :D , выполнение каких-то очень длительных вычислений в отрыве от других работ в интерфейсе. Нужда в таком возникает не то что не всегда, а, скорее, достаточно редко, в специфических случаях.
Аватара пользователя
Снег Север
долгожитель
 
Сообщения: 2233
Зарегистрирован: 27.11.2007 16:14:47

Re: Отображение информациии во время выполнения цикла

Сообщение zoltanleo » 04.11.2019 09:32:03

olegy123 писал(а):Synchronize это способ правильно разрулить и развести когда есть необходимость влезть в сферу главного потока. Принудительно вызывать в нем Application.ProcessMessages - это неправильно.


Мне кажется, мы спорим о разных вещах. Application.ProcessMessages в данном коде - это возможность основному потоку делать все свои привычные дела, пока доп.поток делает какие-то свои темные делишки. Ты можешь вообще в доп.потоке использовать Queue(@...) вместо Synchronize, тогда доп.поток совсем перестанет обращать внимание на окружающий мир :D

По поводу TTimer, я согласен со Снег Север - штука достаточная специфичная и применяться она должна в зависимости от текущей задачи.

Если уж тебя так смущает ProcessMessages в цикле главного потока, поставь в коде после него sleep(...) - будет полная имитация твоего любимого таймера :)
Аватара пользователя
zoltanleo
постоялец
 
Сообщения: 332
Зарегистрирован: 17.10.2013 10:55:01

Re: Отображение информациии во время выполнения цикла

Сообщение olegy123 » 04.11.2019 12:15:36

Я про то что в данном вопросе лучше использовать TTimer для обновления TProgressBar и других значков.
Самый простой и верный способ.

Можно использовать TThread но тогда нужно понимать как происходит межпроцессорное взаимодействие.
TTimer в основе лежит TThread, создатели свели к минимуму писанины, для программиста требуется просто указать что нужно делать в событии TTimer.OnTimer
olegy123
энтузиаст
 
Сообщения: 1496
Зарегистрирован: 25.02.2016 12:10:20


Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 7

Рейтинг@Mail.ru