Библиотеки: Multi Timer

 

Multi Timer:

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

Multi Timer

Автор: Nikolai Semko

 
Версия 1.02 
теперь не нужно создавать экземпляр класса, т.к. он уже создан с именем "timers"
Функции OnTimer также не должно быть в теле программы. 
См. описание.
 

что-то мне шепчет что к методу KillTimer __нельзя__ обращаться из функции-обработчика.

Любимый тут ArrayRemove собъёт цикл опроса массива и получится ошибка "фик поймаешь всемером" :-) 

 
Maxim Kuznetsov:

что-то мне шепчет что к методу KillTimer __нельзя__ обращаться из функции-обработчика.

Любимый тут ArrayRemove собъёт цикл опроса массива и получится ошибка "фик поймаешь всемером" :-) 

Ну конечно преувеличиваете проблему.
Я думал об этом. 
Максимум, что светит, в случае когда одновременно подошло время двух таймеров, стоящих друг за другом в массиве, это то, что второй таймер будет в пропущен в текущем цикле, а сработает в следующем, т.е. через 15,625 милисекунд. 

Впрочем, это тоже не порядок. Доработал в версии 1.03.
Спасибо.

 
Nikolai Semko:

Ну конечно преувеличиваете проблему.
Я думал об этом. 
Максимум, что светит, в случае когда одновременно подошло время двух таймеров, стоящих друг за другом в массиве, это то, что второй таймер будет в пропущен в текущем цикле, а сработает в следующем, т.е. через 15,625 милисекунд. 

Впрочем, это тоже не порядок. Доработал в версии 1.03.
Спасибо.

только не говори, что я придираюсь :-)

если выставить таймер в 100 msc, и уйти в торговую операцию на 15 сек (а это нормально, бывает и больше), то таймер потом начнёт щёлкать каждые 15 msc пока не нагонит. 

фича конечно, только странная. Например для опросов по таймеру даже вредная. 

 
Maxim Kuznetsov:

только не говори, что я придираюсь :-) 

если выставить таймер в 100 msc, и уйти в торговую операцию на 15 сек (а это нормально, бывает и больше), то таймер потом начнёт щёлкать каждые 15 msc пока не нагонит. 

фича конечно, только странная. Например для опросов по таймеру даже вредная. 

Откуда такая инфа про нагонит?
Ты вообще в курсе, что такое системные прерывания?

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

 
Версия 1.04
Добавлена функция, с помощью которой можно получить количество пропущенных событий таймера для контроля стабильности работы:
   int GetLost(int milliseconds, TFunc fun);    // Получаем количество пропущенных событий таймера для контроля стабильности работы.
                                                // Если ноль, то пропусков нет. Если -1, то не найден такой таймер.
 

Версия 1.05
Добавлена функция, с помощью которой можно изменить период одного из конкретных таймеров: 

   void NewPeriod(int old_milliseconds, TFunc fun, int new_milliseconds); // Меняет периодичность данного таймера. Это позволяет создавать таймеры
                                                                          // с динамическим периодом обращения, или автоподстройкой.
 
не совсем понимаю, зачем в строке 61
if (_n>=mt[i].n) {

применяется ">=". В этом случае происходит вызов fun сразу после добавления нового таймера, не дожидаясь того самого времени, которое мы задаем.

Если поменять на ">", то как раз-таки таймер ждет указанное ему время, а затем уже вызывает fun


Так же изменил GetTickCount на GetTickCount64. Терминал работает 24/7, чувствую, что через 2 месяца некоторые таймеры сломались бы
 
Dzmitry Kokhanau #:
(_n>=mt[i].n)

Дмитрий, спасибо — оба замечания в точку, обновил библиотеку до 1.07.

По  GetTickCount  вы правы полностью, и там даже хуже, чем кажется.  GetTickCount  переполняется через 49.7 суток, но из-за строки  if (mt[i].start>t0) continue;  это не разовый сбой: после переполнения  t0  начинает отсчёт заново, а  start  остаётся большим и уже никогда не обновляется — таймер замолкает навсегда. Перешёл на  GetTickCount64 , поля времени перевёл в  ulong .

По  >=  вы верно увидели симптом: первый вызов действительно происходит сразу, не дожидаясь периода. Но замена на  >  лечит только первую итерацию, а дальше ломает работу. Причина в строке  mt[i].n = v3+1  — она рассчитана на нестрогое сравнение. При строгом следующее срабатывание требует прироста сразу на два периода, то есть период удваивается, и вдобавок  lost  растёт на единицу с каждым вызовом, показывая пропуски, которых нет —  GetLost()  начинает врать.

Правильное место — не условие, а инициализация: в  NewTimer  теперь  mt[N].n = 1  вместо  0 . Первый вызов приходится ровно на конец первого периода, периодичность и счётчик пропусков остаются корректными.

Заодно поправил ещё два места: все три перегрузки  KillTimer  пропускали дубликаты ( RemoveTimer  переставляет в позицию  i  последний элемент, а  i++  через него перешагивал), и дескриптор таймера стал отдельным счётчиком вместо момента создания — раньше два таймера, созданных в одну миллисекунду, были для  KillTimer  неразличимы.

Спасибо за внимательное чтение.

Файлы:
Timer.mqh  12 kb
 
добавил новую версию 1.08 и изменил описание