Принципы работы TTimer
Модератор: Модераторы
Принципы работы TTimer
Здравствуйте.
Скажите пожалуйста каков алгоритм работы таймеров в ситуации когда их несколько в программе, скажем 3шт?
И программа однопоточная служба windows.
1.
Правильно понимаю.
Если например пришло время срабатывания таймеров 2 и 3 в то время когда еще длится обработка OnTimer на таймере1.
То они не срабатывают, а запаздывают пока не закончится процедура обработки таймера1
А дальше из 2 и 3 срабатывает тот у кого раньше наступило время планового срабатывания?
2.
Похожий вопрос.
Нужно ли в начале обработчика таймера выключать его и в конце процедуры снова включать?
Т.е. может ли таймер снова сработать (по своему времени) пока не закончилась обработка его собственного события OnTimer ?
Скажите пожалуйста каков алгоритм работы таймеров в ситуации когда их несколько в программе, скажем 3шт?
И программа однопоточная служба windows.
1.
Правильно понимаю.
Если например пришло время срабатывания таймеров 2 и 3 в то время когда еще длится обработка OnTimer на таймере1.
То они не срабатывают, а запаздывают пока не закончится процедура обработки таймера1
А дальше из 2 и 3 срабатывает тот у кого раньше наступило время планового срабатывания?
2.
Похожий вопрос.
Нужно ли в начале обработчика таймера выключать его и в конце процедуры снова включать?
Т.е. может ли таймер снова сработать (по своему времени) пока не закончилась обработка его собственного события OnTimer ?
-
xchgeaxeax
- постоялец
- Сообщения: 207
- Зарегистрирован: 11.05.2023 02:51:40
Именно в Windows таймеры работают через очередь сообщений ветви. Т.е. по наступлению времени срабатывания происходит "PostMessage(hwnd, WM_TIMER, id, lparam)".
Дальше из очереди сообщений его вытащит цикл обработки сообщений (Application.Run или Application.ProcessMessages) и отправит на обработку в соответствующую процедуру OnTimer.
Разумеется, до окончания выполнения кода процедуры, выполнение еще одного срабатывания (т.е. повторный запуск кода даже того же самого таймера или любого другого) не произойдет пока дело не дойдет до цикла обработки сообщений (Application.Run или Application.ProcessMessages). До Application.Run дойти можно только закончив исполнение процедуры по Exit, а вот Application.ProcessMessages может встретиться внутри процедуры обработки явно или не явно в одной из подпрограмм используемых для обработки события. В любом случае писать длинное по времени обработки событие не стоит т.к. это снизит кликабельность интерфейса.
Дальше из очереди сообщений его вытащит цикл обработки сообщений (Application.Run или Application.ProcessMessages) и отправит на обработку в соответствующую процедуру OnTimer.
Разумеется, до окончания выполнения кода процедуры, выполнение еще одного срабатывания (т.е. повторный запуск кода даже того же самого таймера или любого другого) не произойдет пока дело не дойдет до цикла обработки сообщений (Application.Run или Application.ProcessMessages). До Application.Run дойти можно только закончив исполнение процедуры по Exit, а вот Application.ProcessMessages может встретиться внутри процедуры обработки явно или не явно в одной из подпрограмм используемых для обработки события. В любом случае писать длинное по времени обработки событие не стоит т.к. это снизит кликабельность интерфейса.
Последний раз редактировалось xchgeaxeax 02.10.2024 20:13:41, всего редактировалось 1 раз.
1. Частично правильно. В однопоточной (однопроцессорной) системе именно так и происходит.
В многопоточной системе таймера могут работать одновременно, если они не работают с одними и теми же данными. Если обрабатываемые данные используются в нескольких таймерах, то таймера не должны работать одновременно. Но следить за этим должны вы сами (есть некоторые данные за которыми следит сама система).
2. Не обязательно. Но да, такое возможно. Поэтому если процесс не долгий, то зачастую беспокоится не о чем.
Таймера обычно надо отключать когда таймера могут перекрывать друг друга или какие-то процессы, которые происходят "без вашего ведома" в программе. Обычно приходит с опытом, когда нужно отключать, а когда нет.
В многопоточной системе таймера могут работать одновременно, если они не работают с одними и теми же данными. Если обрабатываемые данные используются в нескольких таймерах, то таймера не должны работать одновременно. Но следить за этим должны вы сами (есть некоторые данные за которыми следит сама система).
2. Не обязательно. Но да, такое возможно. Поэтому если процесс не долгий, то зачастую беспокоится не о чем.
Таймера обычно надо отключать когда таймера могут перекрывать друг друга или какие-то процессы, которые происходят "без вашего ведома" в программе. Обычно приходит с опытом, когда нужно отключать, а когда нет.
-
xchgeaxeax
- постоялец
- Сообщения: 207
- Зарегистрирован: 11.05.2023 02:51:40
Только если таймеры созданы в разных потоках. Все таймеры из одного потока будут работать как в однопоточной системе. И даже те, что используют указатель на процедуру исполнения, а не идентификатор окна и идентификатор таймера. Передачей управления в процедуру все равно занимается цикл обработки сообщений (если мы говорим про стандартный TTimer).Seenkao писал(а):В многопоточной системе таймера могут работать одновременно, если они не работают с одними и теми же данными. Если обрабатываемые данные используются в нескольких таймерах, то таймера не должны работать одновременно. Но следить за этим должны вы сами (есть некоторые данные за которыми следит сама система).
Очень хотелось бы увидеть пример подобной необходимости у стандартного TTimer в однопоточном приложении.Seenkao писал(а):2. Не обязательно. Но да, такое возможно. Поэтому если процесс не долгий, то зачастую беспокоится не о чем.
Таймера обычно надо отключать когда таймера могут перекрывать друг друга или какие-то процессы, которые происходят "без вашего ведома" в программе. Обычно приходит с опытом, когда нужно отключать, а когда нет.
Добавлено спустя 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.У вас нет необходимых прав для просмотра вложений в этом сообщении.
Последний раз редактировалось xchgeaxeax 02.10.2024 14:04:01, всего редактировалось 1 раз.
Получается в обработке OnTimer лучше не использовать Application.ProcessMessagesxchgeaxeax писал(а):Дальше из очереди сообщений его вытащит цикл обработки сообщений (Application.Run или Application.ProcessMessages) и отправит на обработку в соответствующую процедуру OnTimer.
Спасибо.
Добавлено спустя 2 минуты 29 секунд:
Seenkao писал(а):Таймера обычно надо отключать когда таймера могут перекрывать друг друга или какие-то процессы, которые происходят "без вашего ведома" в программе. Обычно приходит с опытом, когда нужно отключать, а когда нет.
Решил спросить об этом на всякий случай, мне когда то "показалось", что происходит накладка обработок и я стал всегда писать выключение таймера вначале и включение в конце обработки. Видимо не показалось и такое возможно (видимо использовал прерывание Application.ProcessMessages не помню уже)
Добавлено спустя 1 минуту 49 секунд:
Это понятно. Но так как мне обычно нужно не срабатывание в конкретное время, а обработка через определенные интервалы, то это устраиваетxchgeaxeax писал(а):Подобные действия зачастую накапливают задержки по времени работы таймера.
------
Спасибо всем за ответы.
-
xchgeaxeax
- постоялец
- Сообщения: 207
- Зарегистрирован: 11.05.2023 02:51:40
Забыл в проверочном коде на Lazarus добавить задержку между iStart и iStop. Поэтому так не однозначно получилось. Исправил скриншоты
Добавлено спустя 1 час 58 минут 31 секунду:
Еще я попытался получить на Delphi и на Lazarus повторное исполнение кода в процедуре таймера до его завершения. У меня получилось.
Добавлено спустя 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.У вас нет необходимых прав для просмотра вложений в этом сообщении.
Последний раз редактировалось xchgeaxeax 02.10.2024 19:40:52, всего редактировалось 1 раз.
не только задержки, но есть код, который использует одни и те же данные. И в некоторых ситуациях важно, чтоб таймера шли друг за другом и не иначе.xchgeaxeax писал(а):Подобные действия зачастую накапливают задержки по времени работы таймера. Если его не трогать, то следующее срабатывание произойдет точно через заданный интервал времени без учета времени выполнения процедуры обработки таймера. При выключении и включении у вас будут накапливаться задержки на исполнение кода процедуры обработки события от таймера между интервалами срабатывания.
Какими-то стандартными средствами такое провернуть сложно. Разработчики Lazarus уже не одну тысячу вариантов просмотрели, чтоб ни один из вариантов не сработал. Если только специально вручную это делать, то на такой момент нарваться можно.xchgeaxeax писал(а):Еще я попытался получить на Delphi и на Lazarus повторное исполнение кода в процедуре таймера до его завершения. У меня не получилось.
Допустим ввести зависимость одного таймера от другого, и получим в конце зависание программы (на самом деле это сработает и в стандартных методах - получается я обманул что сложно это сделать?).
Это не попытка спора, это дополнение к сказанному вами!
-
xchgeaxeax
- постоялец
- Сообщения: 207
- Зарегистрирован: 11.05.2023 02:51:40
Опять все же банальные опечатки, которые я не сразу заметил (спасибо за коммент - заставили перечитать мой код)...
Повторные срабатывания у таймера получить удалось. И 7 дельфа в этом плане вела себя стабильнее лазаря 3.0. Скриншоты и код исправил.
Повторные срабатывания у таймера получить удалось. И 7 дельфа в этом плане вела себя стабильнее лазаря 3.0. Скриншоты и код исправил.
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 11:10:32, всего редактировалось 2 раза.
-
xchgeaxeax
- постоялец
- Сообщения: 207
- Зарегистрирован: 11.05.2023 02:51:40
Для стандартного TTimer без входа в цикл обработки сообщений этого не будет Как можете видеть, до Timer1.Tag = 1 обработчики запускались последовательно, но между запусками была задержка в 1 секунду.Alex2013 писал(а):1 Повторный запуск кода обработчика события от таймера по истечению времени вполне себе происходит даже если код предыдущего вызова не завершен ( и часто это нужно учитывать отключая таймер вначале и включая в конце кода обработчика )
После переключения Timer1.Tag = 1 обработчики все так же запускаются последовательно, но задержка между запусками отсутствует т.к. предыдущая обработка занимает по времени больше чем интервал срабатывания таймера. И в однопоточной программе параллельных таймеров быть не может. Некому переключать контексты исполнения кода т.к. поток (читай контекст) всего один.
Это я просто в предыдущем коде убрал вызов Application.ProcessMessages, чтобы не происходило обработка очереди сообщений.
Добавлено спустя 18 минут 47 секунд:
Если вы хотите говорить о многопоточных программах, то там стандартный TTimer будет вести себя точно также как и в однопоточной. А вот множественные срабатывания процедур обработки таймеров возможно только если вы из разных потоков назначили разные таймеры на один обработчик.
У вас нет необходимых прав для просмотра вложений в этом сообщении.
1 Вывод из кода обработчика это еще не работа кода . (Как я писал там вполне может быть "иллюзия очереди" )
2 Да гарантии что повторный вызов сработает вовремя работы обработчика нет, но нет и гарантии что повторного срабатывания не будет, а вот это уже критично .
3 "Чистый код" примера это одно, а реальный код приложения другое (тот-же Application.ProcessMessages может вызваться и явно (например для четкого отображения результатов работы обработчика) так и не явно в каких нибудь "дебрях LCL" )
Добавлено спустя 22 минуты 40 секунд:
Но справедливости ради, стоит отметит, что вызвать в обработчике все тот-же "Application.ProcessMessage" никто (и ничто) вроде не запрещает, так что "одноразовый таймер" вполне может быть один на каждый поток, что разумеется будет надежнее (бо черт его знает, что будет, если создание таймеров как-нибудь криво "пересечется во времени" ).
2 Да гарантии что повторный вызов сработает вовремя работы обработчика нет, но нет и гарантии что повторного срабатывания не будет, а вот это уже критично .
3 "Чистый код" примера это одно, а реальный код приложения другое (тот-же Application.ProcessMessages может вызваться и явно (например для четкого отображения результатов работы обработчика) так и не явно в каких нибудь "дебрях LCL" )
Добавлено спустя 22 минуты 40 секунд:
Разумеется это правда и именно это я имел ввиду когда писал о "одноразовом таймере " (создали, отработал, остановился и "само убился" ) - но незабываем что создание и запуск таймеров происходит в другом потоке(относительно кода обработчика), однако, разумеется это работает если создание таймера не происходит слишком часто, иначе все быстро упрется в лимит таймеров .xchgeaxeax писал(а):Если вы хотите говорить о многопоточных программах, то там стандартный TTimer будет вести себя точно также как и в однопоточной. А вот множественные срабатывания процедур обработки таймеров возможно только если вы из разных потоков назначили разные таймеры на один обработчик.
Но справедливости ради, стоит отметит, что вызвать в обработчике все тот-же "Application.ProcessMessage" никто (и ничто) вроде не запрещает, так что "одноразовый таймер" вполне может быть один на каждый поток, что разумеется будет надежнее (бо черт его знает, что будет, если создание таймеров как-нибудь криво "пересечется во времени" ).
-
xchgeaxeax
- постоялец
- Сообщения: 207
- Зарегистрирован: 11.05.2023 02:51:40
Это ваша иллюзия очереди просто противоречит принципу работы треда. Не может контекст прыгать по коду как захочет. У вас тогда все будет сломано. Если код запустился, то он обязан дойти до конца, чтобы сработало как выделение ресурсов так и освобождение. В противном случае у вас потечет память.Alex2013 писал(а):Вывод из кода обработчика это еще не работа кода . (Как я писал там вполне может быть "иллюзия очереди" )
А это может случиться только если какие-то левые компоненты (не из LCL) или ваши классы не явно приходят в цикл обработки сообщений (используют Application.ProcessMessages). Стандартный TTimer реализует обертку WinAPI SetTimer. Подробнее о работе этих таймеров читайте в MSDN.Alex2013 писал(а):Да гарантии что повторный вызов сработает вовремя работы обработчика нет, но нет и гарантии что повторного срабатывания не будет, а вот это уже критично .
В дебрях не может т.к. графические компоненты просто опираются на то, что вы в любом случае по завершении придете в Application.Run. А вот левые компоненты - вполне могут это использовать.Alex2013 писал(а):так и не явно в каких нибудь "дебрях LCL" )
Application.ProcessMessages обработает сообщения от таймера только для того потока, в котором создан этот самый объект Application. Для других потоков таймеры будут работать с очередью сообщений этого потока (такая очередь сообщений для каждого потока своя, но вы не обязаны ей пользоваться). Для запуска таймеров без обработки событий очереди можно использовать https://learn.microsoft.com/ru-ru/windo ... abletimerwAlex2013 писал(а):Разумеется это правда и именно это я имел ввиду когда писал о "одноразовом таймере " (создали, отработал, остановился и "само убился" ) - но незабываем что все это происходит в другом потоке, однако, разумеется это работает если создание таймера не происходит слишком часто иначе все быстро упрется в лимит таймеров )) , но справедливости ради стоит отметит, что вызвать в обработчике все тот-же "Application.ProcessMessage" никто (и ничто) вроде не запрещает так что "одноразовый таймер" вполне может быть один на каждый поток что разумеется будет надежнее (бо черт его знает что будет если создание таймеров как-нибудь криво "пересечется во времени" ).
... Ладно, вероятно вы правы, так что буду считать что это моя личная "коллекция заблуждений".xchgeaxeax писал(а):В дебрях не может т.к. графические компоненты просто опираются на то, что вы в любом случае по завершении придете в Application.Run. А вот левые компоненты - вполне могут это использовать.
В дебрях не может т.к. графические компоненты просто опираются на то, что вы в любом случае по завершении придете в Application.Run. А вот левые компоненты - вполне могут это использовать.
О а вот это интересно (пробовал запускать таймер через WinAPI)xchgeaxeax писал(а):Application.ProcessMessages обработает сообщения от таймера только для того потока, в котором создан этот самый объект Application. Для других потоков таймеры будут работать с очередью сообщений этого потока (такая очередь сообщений для каждого потока своя, но вы не обязаны ей пользоваться). Для запуска таймеров без обработки событий очереди можно использовать https://learn.microsoft.com/ru-ru/windo ... abletimerw
Примерно так
Код: Выделить всё
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);
-
xchgeaxeax
- постоялец
- Сообщения: 207
- Зарегистрирован: 11.05.2023 02:51:40
Только там должна быть процедура типа stdcall и с 4 параметрами.Alex2013 писал(а):Примерно так
Код: Выделить всё
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.Добавлено спустя 1 минуту 16 секунд:
Если не догадаетесь, то скажу через некоторое время.
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Ладно убедили ....
( хотя я не особо и не спорил ... Просто действительно сталкивался с ситуациями когда без "заглушки" (проверки повторного вызова или отключения таймера внутри обработчика ) обработчик таймера не работал нормально, а в причины особо не вникал )
Зы
Свежий пример ( можно придраться и сказать, что де "это другое", но тем не менее таймер отключается в первую очередь, а если переписать код "более компактно" без предварительного выключения таймера то он работает заметно менее стабильно )...
Добавлено спустя 42 минуты 24 секунды:
Перезапускается отсчет?
iTimer := SetTimer(0, 0, 1000, @TimerProc); ?
нет Application.ProcessMessages? (while iWait > GetTickCount64 do begin Application.ProcessMessages; Sleep(10); end)
Одноядерный проц или принудительно оключены ядра доступные приложению ?
Зы
Свежий пример ( можно придраться и сказать, что де "это другое", но тем не менее таймер отключается в первую очередь, а если переписать код "более компактно" без предварительного выключения таймера то он работает заметно менее стабильно )...
Код: Выделить всё
// ВИ Таймер для полной инициализации браузера
procedure TMainForm.Timer1Timer(Sender: TObject);
begin
Timer1.Enabled := False;
if GlobalWebView2Loader.Initialized then
WVBrowser1.CreateBrowser(WVWindowParent1.Handle)
else
Timer1.Enabled := True;
end;
(Вникать лень поэтому "включаю мозговой штурм " (то бишь " предположения от балды" ) )xchgeaxeax писал(а):Если не догадаетесь, то скажу через некоторое время.
Перезапускается отсчет?
iTimer := SetTimer(0, 0, 1000, @TimerProc); ?
нет Application.ProcessMessages? (while iWait > GetTickCount64 do begin Application.ProcessMessages; Sleep(10); end)
Одноядерный проц или принудительно оключены ядра доступные приложению ?
