Войти
ПрограммированиеФорумОбщее

[.NET Framework] Нужен надёжный таймер

#0
16:41, 28 фев. 2018

Есть такая проблема. Создаю таймер, задаю ему интервал обновления 100 мс, всё работает. Потом перевожу системные часы на час назад и таймер перестаёт работать, пока час не пройдёт.
Такая проблема с обоими таймерами: System.Timers.Timer и System.Threading.Timer. Есть что-то понадёжнее в C#? В голову приходит только поток со Sleep.
На винде такой проблемы нет, но она есть на андроиде (в Unity) и очень фатальна. Корутины не предлагать, нужно в отдельном потоке, не нагружая и без того тормозной основной поток.


#1
18:10, 28 фев. 2018

Проверенный способ - на тиках/апдейтах прибавлять длительность тика и проверять суммарное значение.

#2
19:01, 28 фев. 2018

MANAB
> Проверенный способ - на тиках/апдейтах прибавлять длительность тика и проверять
> суммарное значение.
Проверять где? Таймер (Timer, который с колбеком, не путать с Stopwatch) же дёргает система, а если время перевести назад, то она перестанет его дёргать - колбек перестанет вызываться.

#3
4:17, 1 мар. 2018

gammaker
> Корутины не предлагать, нужно в отдельном потоке, не нагружая и без того
> тормозной основной поток.
Если ничего больше не подойдет - предлагаю сделать очередь задач, типо List<Task> или чтото по-технологичней, запустить Thread который будет из очереди брать Task или блочиться, такая абстракция BlockingQueue. А добавлять туда Task из основного потока через корутину или (что тоже самое) через Update() с заданной периодичностью, наверняка unity Time.deltaTime норм будет отрабатывать.

В голову приходит только поток со Sleep.

собственно почти тоже самое, отличие (иногда влияет) в том что при Sleep(Х) будет отсчитываться Х с момента окончания задачи, а при подходе с постановкой в очередь - постановка через одинаковый интервал Х, ну и Sleep собственно проще реализовать
#4
11:05, 1 мар. 2018

Вообще это странное поведение. Наверняка таймер там сделан на condition variable. И судя по исходникам, оно должно использовать CLOCK_MONOTONIC:
https://github.com/mono/mono/blob/master/mono/utils/mono-os-mutex.h#L143
CLOCK_MONOTONIC, в отличии от дефолтного CLOCK_REALTIME, должен корректно отрабатывать перевод времени, я как-то это проверял.

Update: перевод на CLOCK_MONOTONIC случился относительно недавно
https://github.com/mono/mono/commit/f9e87491df2a7ead48790a36170556c4fde6cebe
возможно, что в твоей версии этих изменений еще нет.

#5
12:58, 1 мар. 2018

Ghost2
> перевод на CLOCK_MONOTONIC случился относительно недавно
> https://github.com/mono/mono/commit/f9e87491df2a7ead48790a36170556c4fde6cebe
> возможно, что в твоей версии этих изменений еще нет.
Спасибо, теперь всё понятно. Это же Unity, там всегда древний Mono был. Хотя там вроде какой-то новый экспериментальный можно выбрать, надо будет попробовать.

Alex_Hell
> предлагаю сделать очередь задач, типо List<Task> или чтото по-технологичней,
> запустить Thread который будет из очереди брать Task или блочиться, такая
> абстракция BlockingQueue. А добавлять туда Task из основного потока через
> корутину или (что тоже самое) через Update() с заданной периодичностью,
> наверняка unity Time.deltaTime норм будет отрабатывать.
Вроде хорошая идея, наверное так и сделаю, если выбор экспериментальной версии Mono в Unity не поможет.

#6
15:24, 1 мар. 2018

gammaker
> Проверять где? Таймер (Timer, который с колбеком, не путать с Stopwatch) же
> дёргает система, а если время перевести назад, то она перестанет его дёргать -
> колбек перестанет вызываться.
У тебя есть Unity, в нем есть MonoBehaviour с Update и FixedUpdate, а также объектTime со свойствами deltaTime и fixedDeltaTime. Далее код:

private float _timer = 0;

public float Period {get; private set; }

void Update()
{
  _timer += Time.deltaTime;
  
  if (_timer >= Period)
  {
    _timer -= Period;
    // event here!
  }
}

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

#7
15:46, 1 мар. 2018

MANAB
> Конечно, я помню про то, что ты не хотел дергать в основном потоке, но, имхо,
> он не займет времени больше, чем синхронизация основного потока и потока
> таймера в момент события (ты ж из другого потока наверняка хочешь что-то
> сделать в главном потоке игры?)
Там происходит общение с внешним устройством, которое может тормозить и подвисать. Держать такое в основном потоке нельзя. В результате обмена данными какие-то данные возможно нужно будет отобразить, но 90% полученных данных не нужно отображать сразу, поэтому блокировок особо быть не должно.

#8
16:56, 1 мар. 2018

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

#9
22:49, 1 мар. 2018

gammaker
> Там происходит общение с внешним устройством, которое может тормозить и
> подвисать. Держать такое в основном потоке нельзя. В результате обмена данными
> какие-то данные возможно нужно будет отобразить, но 90% полученных данных не
> нужно отображать сразу, поэтому блокировок особо быть не должно.
Ок, тогда можно оставить этот лайтовый таймер в основном потоке и из него отправлять сообщения о тайминге дополнительному потоку (паттерн поставщик-потребитель).

#10
23:25, 1 мар. 2018

Zab
> Не сразу вернет, потому как она попытается делать это без скачков
Вот про это не знал, спасибо за инфу.

MANAB
> Ок, тогда можно оставить этот лайтовый таймер в основном потоке и из него
> отправлять сообщения о тайминге дополнительному потоку (паттерн
> поставщик-потребитель).
Это уже предложили, завтра проверю новую версию Mono и если не поможет, то этот вариант сделаю.

#11
11:05, 2 мар. 2018

Вариант с обновлением Mono не прокатил. С новой версией Mono всё зависает на самом первом кадре, в логе пусто, где искать ошибку, непонятно. В редакторе всё хорошо, а вот на Android устройстве такая фигня. Редактор вообще как работает? Через .NET Framework или тоже Mono?

Правка: собрал Windows версию, на ней тоже работает, а при сборке было указано, что Mono. Значит видимо дело именно в реализации Mono под Android.

#12
10:26, 3 мар. 2018

gammaker
напиши свой таймер короче и не парься. пиши только под конкретные оси где проблемы. а дальше обертку над ними и стандартным

#13
14:51, 3 мар. 2018

*Lain*
> напиши свой таймер короче и не парься. пиши только под конкретные оси где
> проблемы. а дальше обертку над ними и стандартным
Я в итоге так и сделал, использовал вот этот класс: https://developer.android.com/reference/java/util/concurrent/Sche… Executor.html
Всё нормально работает при переводе часов, проблема решена.

ПрограммированиеФорумОбщее

Тема в архиве.