Принципы работы TTimer

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

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

Принципы работы TTimer

Сообщение jsa » 02.10.2024 12:04:25

Здравствуйте.
Скажите пожалуйста каков алгоритм работы таймеров в ситуации когда их несколько в программе, скажем 3шт?
И программа однопоточная служба windows.

1.
Правильно понимаю.
Если например пришло время срабатывания таймеров 2 и 3 в то время когда еще длится обработка OnTimer на таймере1.
То они не срабатывают, а запаздывают пока не закончится процедура обработки таймера1
А дальше из 2 и 3 срабатывает тот у кого раньше наступило время планового срабатывания?

2.
Похожий вопрос.
Нужно ли в начале обработчика таймера выключать его и в конце процедуры снова включать?
Т.е. может ли таймер снова сработать (по своему времени) пока не закончилась обработка его собственного события OnTimer ?
jsa
постоялец
 
Сообщения: 282
Зарегистрирован: 28.11.2017 13:46:04

Re: Принципы работы TTimer

Сообщение xchgeaxeax » 02.10.2024 12:21:39

Именно в Windows таймеры работают через очередь сообщений ветви. Т.е. по наступлению времени срабатывания происходит "PostMessage(hwnd, WM_TIMER, id, lparam)".
Дальше из очереди сообщений его вытащит цикл обработки сообщений (Application.Run или Application.ProcessMessages) и отправит на обработку в соответствующую процедуру OnTimer.
Разумеется, до окончания выполнения кода процедуры, выполнение еще одного срабатывания (т.е. повторный запуск кода даже того же самого таймера или любого другого) не произойдет пока дело не дойдет до цикла обработки сообщений (Application.Run или Application.ProcessMessages). До Application.Run дойти можно только закончив исполнение процедуры по Exit, а вот Application.ProcessMessages может встретиться внутри процедуры обработки явно или не явно в одной из подпрограмм используемых для обработки события. В любом случае писать длинное по времени обработки событие не стоит т.к. это снизит кликабельность интерфейса.
Последний раз редактировалось xchgeaxeax 02.10.2024 21:13:41, всего редактировалось 1 раз.
xchgeaxeax
постоялец
 
Сообщения: 125
Зарегистрирован: 11.05.2023 03:51:40

Re: Принципы работы TTimer

Сообщение Seenkao » 02.10.2024 13:20:52

1. Частично правильно. В однопоточной (однопроцессорной) системе именно так и происходит.
В многопоточной системе таймера могут работать одновременно, если они не работают с одними и теми же данными. Если обрабатываемые данные используются в нескольких таймерах, то таймера не должны работать одновременно. Но следить за этим должны вы сами (есть некоторые данные за которыми следит сама система).

2. Не обязательно. Но да, такое возможно. Поэтому если процесс не долгий, то зачастую беспокоится не о чем.
Таймера обычно надо отключать когда таймера могут перекрывать друг друга или какие-то процессы, которые происходят "без вашего ведома" в программе. Обычно приходит с опытом, когда нужно отключать, а когда нет.
Seenkao
энтузиаст
 
Сообщения: 526
Зарегистрирован: 01.04.2020 03:37:12

Re: Принципы работы TTimer

Сообщение xchgeaxeax » 02.10.2024 13:37:22

Seenkao писал(а):В многопоточной системе таймера могут работать одновременно, если они не работают с одними и теми же данными. Если обрабатываемые данные используются в нескольких таймерах, то таймера не должны работать одновременно. Но следить за этим должны вы сами (есть некоторые данные за которыми следит сама система).

Только если таймеры созданы в разных потоках. Все таймеры из одного потока будут работать как в однопоточной системе. И даже те, что используют указатель на процедуру исполнения, а не идентификатор окна и идентификатор таймера. Передачей управления в процедуру все равно занимается цикл обработки сообщений (если мы говорим про стандартный TTimer).

Seenkao писал(а):2. Не обязательно. Но да, такое возможно. Поэтому если процесс не долгий, то зачастую беспокоится не о чем.
Таймера обычно надо отключать когда таймера могут перекрывать друг друга или какие-то процессы, которые происходят "без вашего ведома" в программе. Обычно приходит с опытом, когда нужно отключать, а когда нет.

Очень хотелось бы увидеть пример подобной необходимости у стандартного TTimer в однопоточном приложении.

Добавлено спустя 5 минут 29 секунд:
Seenkao писал(а):2. Не обязательно. Но да, такое возможно. Поэтому если процесс не долгий, то зачастую беспокоится не о чем.
Таймера обычно надо отключать когда таймера могут перекрывать друг друга или какие-то процессы, которые происходят "без вашего ведома" в программе. Обычно приходит с опытом, когда нужно отключать, а когда нет.

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

Добавлено спустя 29 минут 55 секунд:
Код: Выделить всё
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, Menus;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Timer1: TTimer;
    PopupMenu1: TPopupMenu;
    imer1Timer1: TMenuItem;
    imer2Timer1: TMenuItem;
    StartStop1: TMenuItem;
    procedure Timer2Timer(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure imer1Timer1Click(Sender: TObject);
    procedure imer2Timer1Click(Sender: TObject);
    procedure StartStop1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    iStop: Integer;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  iStop := GetTickCount;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  iStart: Integer;
begin
  Timer1.Enabled := False;
  iStart := GetTickCount;
  Memo1.Lines.Add(Format('Ñðàáîòàë òàéìåð: %d (Äåëüòà: %d)', [iStart, iStart - iStop]));
  Sleep(10);
  iStop := GetTickCount;
  Memo1.Lines.Add(Format('Òàéìåð çàêîí÷èë: %d (Äåëüòà: %d)', [iStop, iStop - iStart]));
  Timer1.Enabled := True;
end;

procedure TForm1.Timer2Timer(Sender: TObject);
var
  iStart: Integer;
begin
  iStart := GetTickCount;
  Memo1.Lines.Add(Format('Ñðàáîòàë òàéìåð: %d (Äåëüòà: %d)', [iStart, iStart - iStop]));
  Sleep(10);
  iStop := GetTickCount;
  Memo1.Lines.Add(Format('Òàéìåð çàêîí÷èë: %d (Äåëüòà: %d)', [iStop, iStop - iStart]));
end;

procedure TForm1.StartStop1Click(Sender: TObject);
begin
  if Timer1.Tag = 0 then begin
    Timer1.Enabled := False;
    Timer1.Tag := 1;
    StartStop1.Caption := 'Stop';
  end else begin
    Timer1.Enabled := True;
    Timer1.Tag := 0;
    StartStop1.Caption := 'Start';
  end;
end;

procedure TForm1.imer1Timer1Click(Sender: TObject);
begin
  Timer1.OnTimer := Timer1Timer;
end;

procedure TForm1.imer2Timer1Click(Sender: TObject);
begin
  Timer1.OnTimer := Timer2Timer;
end;

end.
Снимок экрана_20241002_150112.png
Снимок экрана_20241002_135817.png
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Последний раз редактировалось xchgeaxeax 02.10.2024 15:04:01, всего редактировалось 1 раз.
xchgeaxeax
постоялец
 
Сообщения: 125
Зарегистрирован: 11.05.2023 03:51:40

Re: Принципы работы TTimer

Сообщение jsa » 02.10.2024 14:53:10

xchgeaxeax писал(а):Дальше из очереди сообщений его вытащит цикл обработки сообщений (Application.Run или Application.ProcessMessages) и отправит на обработку в соответствующую процедуру OnTimer.

Получается в обработке OnTimer лучше не использовать Application.ProcessMessages
Спасибо.

Добавлено спустя 2 минуты 29 секунд:
Seenkao писал(а):Таймера обычно надо отключать когда таймера могут перекрывать друг друга или какие-то процессы, которые происходят "без вашего ведома" в программе. Обычно приходит с опытом, когда нужно отключать, а когда нет.

Решил спросить об этом на всякий случай, мне когда то "показалось", что происходит накладка обработок и я стал всегда писать выключение таймера вначале и включение в конце обработки. Видимо не показалось и такое возможно (видимо использовал прерывание Application.ProcessMessages не помню уже)

Добавлено спустя 1 минуту 49 секунд:
xchgeaxeax писал(а):Подобные действия зачастую накапливают задержки по времени работы таймера.

Это понятно. Но так как мне обычно нужно не срабатывание в конкретное время, а обработка через определенные интервалы, то это устраивает

------
Спасибо всем за ответы.
jsa
постоялец
 
Сообщения: 282
Зарегистрирован: 28.11.2017 13:46:04

Re: Принципы работы TTimer

Сообщение xchgeaxeax » 02.10.2024 15:04:49

Забыл в проверочном коде на Lazarus добавить задержку между iStart и iStop. Поэтому так не однозначно получилось. Исправил скриншоты

Добавлено спустя 1 час 58 минут 31 секунду:
Еще я попытался получить на Delphi и на Lazarus повторное исполнение кода в процедуре таймера до его завершения. У меня получилось.

Код: Выделить всё
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, Menus;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Timer1: TTimer;
    PopupMenu1: TPopupMenu;
    Disable1: TMenuItem;
    Enable1: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure Disable1Click(Sender: TObject);
  private
    iStop, iHits: Integer;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  iStop := GetTickCount;
  iHits := 0;
  Memo1.Lines.Add(Format('Init = {Hits: %d; Stop: %d}', [iHits, iStop]));
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  iStart, iWait, i: Integer;
begin
  if Timer1.Tag = 0 then Timer1.Enabled := False;
  inc(iHits);
  iStart := GetTickCount;
  iWait := 1500 - iHits * 100;
  Memo1.Lines.Add(Format('Done = {iStart: %d; Delta: %d; Hits: %d; Wait: %d}',
                         [iStart, iStart - iStop, iHits, iWait]));
  if iWait < 10 then iWait := 10;
  inc(iWait, GetTickCount);
  while GetTickCount < iWait do begin // При первом входе в процедуру задержка 1400мс, при втором 1300мс итд минимальная 10мс.
    Sleep(10);
    Application.ProcessMessages;
  end;
  dec(iHits);
  iStop := GetTickCount;
  Memo1.Lines.Add(Format('Done = {iStop: %d; Delta: %d; Hits: %d; Tag: %d}',
                         [iStop, iStop - iStart, iHits, Timer1.Tag]));
  if Timer1.Tag = 0 then Timer1.Enabled := True;
end;

procedure TForm1.Disable1Click(Sender: TObject);
begin
  Timer1.Tag := (Sender as TMenuItem).Tag;
end;

end.
Снимок экрана_20241002_203842.png
Снимок экрана_20241002_203915.png
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Последний раз редактировалось xchgeaxeax 02.10.2024 20:40:52, всего редактировалось 1 раз.
xchgeaxeax
постоялец
 
Сообщения: 125
Зарегистрирован: 11.05.2023 03:51:40

Re: Принципы работы TTimer

Сообщение Seenkao » 02.10.2024 19:34:32

xchgeaxeax писал(а):Подобные действия зачастую накапливают задержки по времени работы таймера. Если его не трогать, то следующее срабатывание произойдет точно через заданный интервал времени без учета времени выполнения процедуры обработки таймера. При выключении и включении у вас будут накапливаться задержки на исполнение кода процедуры обработки события от таймера между интервалами срабатывания.

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

xchgeaxeax писал(а):Еще я попытался получить на Delphi и на Lazarus повторное исполнение кода в процедуре таймера до его завершения. У меня не получилось.

Какими-то стандартными средствами такое провернуть сложно. Разработчики Lazarus уже не одну тысячу вариантов просмотрели, чтоб ни один из вариантов не сработал. Если только специально вручную это делать, то на такой момент нарваться можно.
Допустим ввести зависимость одного таймера от другого, и получим в конце зависание программы (на самом деле это сработает и в стандартных методах - получается я обманул что сложно это сделать?).

Это не попытка спора, это дополнение к сказанному вами!
Seenkao
энтузиаст
 
Сообщения: 526
Зарегистрирован: 01.04.2020 03:37:12

Re: Принципы работы TTimer

Сообщение xchgeaxeax » 02.10.2024 20:43:06

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

Повторные срабатывания у таймера получить удалось. И 7 дельфа в этом плане вела себя стабильнее лазаря 3.0. Скриншоты и код исправил.
xchgeaxeax
постоялец
 
Сообщения: 125
Зарегистрирован: 11.05.2023 03:51:40

Re: Принципы работы TTimer

Сообщение Alex2013 » 03.10.2024 11:12:42

xchgeaxeax писал(а):менно в Windows таймеры работают через очередь сообщений ветви. Т.е. по наступлению времени срабатывания происходит "PostMessage(hwnd, WM_TIMER, id, lparam)".
Дальше из очереди сообщений его вытащит цикл обработки сообщений (Application.Run или Application.ProcessMessages) и отправит на обработку в соответствующую процедуру OnTimer.
Разумеется, до окончания выполнения кода процедуры, выполнение еще одного срабатывания (т.е. повторный запуск кода даже того же самого таймера или любого другого) не произойдет пока дело не дойдет до цикла обработки сообщений (Application.Run или Application.ProcessMessages). До Application.Run дойти можно только закончив исполнение процедуры по Exit, а вот Application.ProcessMessages может встретиться внутри процедуры обработки явно или не явно в одной из подпрограмм используемых для обработки события. В любом случае писать длинное по времени обработки событие не стоит т.к. это снизит кликабельность интерфейса.


1 Повторный запуск кода обработчика события от таймера по истечению времени вполне себе происходит даже если код предыдущего вызова не завершен ( и часто это нужно учитывать отключая таймер вначале и включая в конце кода обработчика или ставя "заглушку" на повторный вызов )

2 Иллюзия "очереди выполнения" иногда возникает просто потому что часть запросов к WinAPI выполняются последовательно, а сам код обработчика вполне может исполнятся параллельно (причем даже на одноядерной машине (ядро одно но потоков все равно куча ))

3 Насколько я знаю одно приложение может иметь ограниченное количество таймеров (( вроде 128 но это неточно )
в отличии от потоков где явные ограничения их по количеству ограничения заметно менее строгие ) .

4 Удобство использования таймеров вместо в потоков в легком доступе к WinApi (кстати есть интересный "хак" в виде использования "одноразового" таймера для вывод данных "из потока " с помощью WinAPI(то бишь стандартных функций LCL) иногда это работает заметно лучше и плавнее временной синхронизации ( повторный вызов прошедший раньше чем нужно в таймере можно игнорить в явном виде, а "синхронизированные процедуры" исполняются последовательно в любом случае ) )
Последний раз редактировалось Alex2013 03.10.2024 12:10:32, всего редактировалось 2 раз(а).
Alex2013
долгожитель
 
Сообщения: 3049
Зарегистрирован: 03.04.2013 11:59:44

Re: Принципы работы TTimer

Сообщение xchgeaxeax » 03.10.2024 11:33:37

Alex2013 писал(а):1 Повторный запуск кода обработчика события от таймера по истечению времени вполне себе происходит даже если код предыдущего вызова не завершен ( и часто это нужно учитывать отключая таймер вначале и включая в конце кода обработчика )

Для стандартного TTimer без входа в цикл обработки сообщений этого не будет
Снимок экрана_20241003_112931.png
Снимок экрана_20241003_112539.png

Как можете видеть, до Timer1.Tag = 1 обработчики запускались последовательно, но между запусками была задержка в 1 секунду.
После переключения Timer1.Tag = 1 обработчики все так же запускаются последовательно, но задержка между запусками отсутствует т.к. предыдущая обработка занимает по времени больше чем интервал срабатывания таймера. И в однопоточной программе параллельных таймеров быть не может. Некому переключать контексты исполнения кода т.к. поток (читай контекст) всего один.

Это я просто в предыдущем коде убрал вызов Application.ProcessMessages, чтобы не происходило обработка очереди сообщений.

Добавлено спустя 18 минут 47 секунд:
Если вы хотите говорить о многопоточных программах, то там стандартный TTimer будет вести себя точно также как и в однопоточной. А вот множественные срабатывания процедур обработки таймеров возможно только если вы из разных потоков назначили разные таймеры на один обработчик.
У вас нет необходимых прав для просмотра вложений в этом сообщении.
xchgeaxeax
постоялец
 
Сообщения: 125
Зарегистрирован: 11.05.2023 03:51:40

Re: Принципы работы TTimer

Сообщение Alex2013 » 03.10.2024 12:23:04

1 Вывод из кода обработчика это еще не работа кода . (Как я писал там вполне может быть "иллюзия очереди" )
2 Да гарантии что повторный вызов сработает вовремя работы обработчика нет, но нет и гарантии что повторного срабатывания не будет, а вот это уже критично .
3 "Чистый код" примера это одно, а реальный код приложения другое (тот-же Application.ProcessMessages может вызваться и явно (например для четкого отображения результатов работы обработчика) так и не явно в каких нибудь "дебрях LCL" )

Добавлено спустя 22 минуты 40 секунд:
xchgeaxeax писал(а):Если вы хотите говорить о многопоточных программах, то там стандартный TTimer будет вести себя точно также как и в однопоточной. А вот множественные срабатывания процедур обработки таймеров возможно только если вы из разных потоков назначили разные таймеры на один обработчик.


Разумеется это правда и именно это я имел ввиду когда писал о "одноразовом таймере " (создали, отработал, остановился и "само убился" ) - но незабываем что создание и запуск таймеров происходит в другом потоке(относительно кода обработчика), однако, разумеется это работает если создание таймера не происходит слишком часто, иначе все быстро упрется в лимит таймеров .

Но справедливости ради, стоит отметит, что вызвать в обработчике все тот-же "Application.ProcessMessage" никто (и ничто) вроде не запрещает, так что "одноразовый таймер" вполне может быть один на каждый поток, что разумеется будет надежнее (бо черт его знает, что будет, если создание таймеров как-нибудь криво "пересечется во времени" ).
Alex2013
долгожитель
 
Сообщения: 3049
Зарегистрирован: 03.04.2013 11:59:44

Re: Принципы работы TTimer

Сообщение xchgeaxeax » 03.10.2024 13:16:05

Alex2013 писал(а):Вывод из кода обработчика это еще не работа кода . (Как я писал там вполне может быть "иллюзия очереди" )

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

Alex2013 писал(а):Да гарантии что повторный вызов сработает вовремя работы обработчика нет, но нет и гарантии что повторного срабатывания не будет, а вот это уже критично .

А это может случиться только если какие-то левые компоненты (не из LCL) или ваши классы не явно приходят в цикл обработки сообщений (используют Application.ProcessMessages). Стандартный TTimer реализует обертку WinAPI SetTimer. Подробнее о работе этих таймеров читайте в MSDN.

Alex2013 писал(а):так и не явно в каких нибудь "дебрях LCL" )

В дебрях не может т.к. графические компоненты просто опираются на то, что вы в любом случае по завершении придете в Application.Run. А вот левые компоненты - вполне могут это использовать.

Alex2013 писал(а):Разумеется это правда и именно это я имел ввиду когда писал о "одноразовом таймере " (создали, отработал, остановился и "само убился" ) - но незабываем что все это происходит в другом потоке, однако, разумеется это работает если создание таймера не происходит слишком часто иначе все быстро упрется в лимит таймеров )) , но справедливости ради стоит отметит, что вызвать в обработчике все тот-же "Application.ProcessMessage" никто (и ничто) вроде не запрещает так что "одноразовый таймер" вполне может быть один на каждый поток что разумеется будет надежнее (бо черт его знает что будет если создание таймеров как-нибудь криво "пересечется во времени" ).

Application.ProcessMessages обработает сообщения от таймера только для того потока, в котором создан этот самый объект Application. Для других потоков таймеры будут работать с очередью сообщений этого потока (такая очередь сообщений для каждого потока своя, но вы не обязаны ей пользоваться). Для запуска таймеров без обработки событий очереди можно использовать https://learn.microsoft.com/ru-ru/windo ... abletimerw
xchgeaxeax
постоялец
 
Сообщения: 125
Зарегистрирован: 11.05.2023 03:51:40

Re: Принципы работы TTimer

Сообщение Alex2013 » 03.10.2024 16:51:58

xchgeaxeax писал(а):В дебрях не может т.к. графические компоненты просто опираются на то, что вы в любом случае по завершении придете в Application.Run. А вот левые компоненты - вполне могут это использовать.
В дебрях не может т.к. графические компоненты просто опираются на то, что вы в любом случае по завершении придете в Application.Run. А вот левые компоненты - вполне могут это использовать.

... Ладно, вероятно вы правы, так что буду считать что это моя личная "коллекция заблуждений". :roll:

xchgeaxeax писал(а):Application.ProcessMessages обработает сообщения от таймера только для того потока, в котором создан этот самый объект Application. Для других потоков таймеры будут работать с очередью сообщений этого потока (такая очередь сообщений для каждого потока своя, но вы не обязаны ей пользоваться). Для запуска таймеров без обработки событий очереди можно использовать https://learn.microsoft.com/ru-ru/windo ... abletimerw

О а вот это интересно (пробовал запускать таймер через WinAPI)
Примерно так
Код: Выделить всё
var Timer:Word;


  Function Work :Boolean;
//точнее procedure  Work (hWnd: THandle; uMsg: UINT; idTimer: Cardinal; dwTime: DWord); stdcall;
// но это уже детали...
  begin
     MessageDlg('Таймер работает...'  ,mtCustom, [mbYes], 0);
     KillTimer(0,Timer);
  end;

begin
  Timer := SetTimer(0, 0, 5 * 1000, @work);
end;
как вариант
TForm1 = class(TForm)
...
procedure WMTImer(var Msg: TMessage); message WM_TIMER;
...
Timer := SetTimer(0, 0, 1000, nil);

В принципе "тоже самое в профиль" единственный плюс в том что нет нужды в классе-обертке ( с вечной проблемой освобождения памяти класса из его же методов ).
Alex2013
долгожитель
 
Сообщения: 3049
Зарегистрирован: 03.04.2013 11:59:44

Re: Принципы работы TTimer

Сообщение xchgeaxeax » 03.10.2024 18:03:29

Alex2013 писал(а):Примерно так

Только там должна быть процедура типа stdcall и с 4 параметрами.
Код: Выделить всё
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Windows, Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    CheckBox1: TCheckBox;
    procedure CheckBox1Change(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private

  public

  end;

var
  Form1: TForm1;
  iTimer, iStop, iHits: Int64;
  bStop: Boolean = False;

implementation

{$R *.lfm}

procedure TimerProc(hWnd: THandle; uMsg: UINT; wPar: QWORD; lPar: LongWord); stdcall;
var
  iDelta, iStart, iWait: Int64;
begin
  if bStop then KillTimer(hWnd, iTimer);
  inc(iHits);
  iStart := GetTickCount64;
  iDelta := iStart - iStop;
  iWait  := iStart + 1500;
  WriteLn('IN {iHits = ', iHits, '; iStart = ', iStart, '; iStop = ', iStop, '; iDelta = ', iDelta, '; iWait = ', iWait, '; bStop = ', BoolToStr(bStop, True), '}');
  while iWait > GetTickCount64 do Sleep(10);
  dec(iHits);
  iStop := GetTickCount64;
  iDelta := iStop - iStart;
  WriteLn('OUT {iHits = ', iHits, '; iStart = ', iStart, '; iStop = ', iStop, '; iDelta = ', iDelta, '; iWait = ', iWait, '; bStop = ', BoolToStr(bStop, True), '}');
  if bStop then iTimer := SetTimer(hWnd, 0, wPar, @TimerProc);
end;

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  iHits := 0;
  bStop := False;
  iStop := GetTickCount64;
  WriteLn('Init {iHits = ', iHits, '; iStop = ', iStop, '; bStop = ', BoolToStr(bStop, True), '}');
  iTimer := SetTimer(0, 0, 1000, @TimerProc);
end;

procedure TForm1.CheckBox1Change(Sender: TObject);
begin
  bStop := CheckBox1.Checked;
end;

end.
Снимок экрана_20241003_180012.png


Как можете видеть, но даже этот таймер не дает выполнять процедуру таймера до завершения предыдущей. Даже, если я его уничтожаю. Но вот еще один рисунок.
Снимок экрана_20241003_180231.png


Как же это у меня получилось?

Добавлено спустя 1 минуту 16 секунд:
Если не догадаетесь, то скажу через некоторое время.
У вас нет необходимых прав для просмотра вложений в этом сообщении.
xchgeaxeax
постоялец
 
Сообщения: 125
Зарегистрирован: 11.05.2023 03:51:40

Re: Принципы работы TTimer

Сообщение Alex2013 » 03.10.2024 19:11:03

Ладно убедили .... :D ( хотя я не особо и не спорил ... Просто действительно сталкивался с ситуациями когда без "заглушки" (проверки повторного вызова или отключения таймера внутри обработчика ) обработчик таймера не работал нормально, а в причины особо не вникал )

Зы
Свежий пример ( можно придраться и сказать, что де "это другое", но тем не менее таймер отключается в первую очередь, а если переписать код "более компактно" без предварительного выключения таймера то он работает заметно менее стабильно )...
Код: Выделить всё
// ВИ Таймер для полной инициализации браузера
procedure TMainForm.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled := False;
  if GlobalWebView2Loader.Initialized then
    WVBrowser1.CreateBrowser(WVWindowParent1.Handle)
   else
    Timer1.Enabled := True;
end;



Добавлено спустя 42 минуты 24 секунды:
xchgeaxeax писал(а):Если не догадаетесь, то скажу через некоторое время.

(Вникать лень поэтому "включаю мозговой штурм " (то бишь " предположения от балды" ) ) :wink:
Перезапускается отсчет?
iTimer := SetTimer(0, 0, 1000, @TimerProc); ?
нет Application.ProcessMessages? (while iWait > GetTickCount64 do begin Application.ProcessMessages; Sleep(10); end)
Одноядерный проц или принудительно оключены ядра доступные приложению ?
Alex2013
долгожитель
 
Сообщения: 3049
Зарегистрирован: 03.04.2013 11:59:44

След.

Вернуться в Lazarus

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

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

Рейтинг@Mail.ru