Как я могу запускать метод через определенные промежутки времени? System.Timers.Timer, кажется, теряет синхронизацию

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

Я хочу рассчитывать и отправлять данные с фиксированным интервалом 0,1 секунды. План состоит в том, чтобы отправить рассчитанные данные, а затем начать расчет следующих данных. Для этого я использовал System.Timers.Timer с AutoReset = true и истекшим событием.

Условия, которых я пытаюсь достичь, такие.

  • Данные не обязательно должны отправляться каждые 0,1 секунды.
  • Предполагается, что рассчитанные данные на данный момент находятся в видео, поэтому внутренний таймер и таймер видео должны быть примерно синхронизированы.
  • Если вычисление данных занимает больше 0,1 секунды, мы просто повторно отправляем последний рассчитанный пакет.

Однако я наткнулся на проблему с синхронизацией таймера. Вот журнал каждый раз, когда я отправляю Sensation, с указанием количества тактов слева и фактического прошедшего времени (определяемого System.Diagnostics.Stopwatch) с номером тика слева и миллисекундами с момента начала выполнения справа.

1 / 106
2 / 216
3 / 326
4 / 448
5 / 554
6 / 660
7 / 768
8 / 875
9 / 983
10 / 1090
11 / 1213
12 / 1322
13 / 1431
14 / 1539
15 / 1647
16 / 1754
17 / 1861
18 / 1968
19 / 2075
20 / 2197
...
154 / 16885
155 / 16994
156 / 17100
157 / 17207
158 / 17317
159 / 17426

По сути, каждый такт в 0,1 секунды, который отправляет данные, выполняется чуть позже, чем 0,1 секунды (что нормально), а следующая задержка отправки складывается поверх этой задержки (что неправильно), поэтому после 15 секунд выполнения таймер уже был около 2 секунды позади.

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

private int tick = 0;

private System.Timers.Timer timer;
private System.Diagnostics.Stopwatch watch;

private Constructor() {
    timer = new System.Timers.Timer(100);
    timer.Elapsed += streamSensation; // simply sends the Sensation (and created above logs)
    timer.Elapsed += calcManagerTick; // mainly calculates the Sensation for next Tick
    timer.AutoReset = true;
    timer.Enabled = false;
}

private void streamSensation(Object source, ElapsedEventArgs e) {
    // Send data
    tick++;
    Console.WriteLine(tick + " / " + watch.ElapsedMilliseconds);
}

private void calcManagerTick(Object source, ElapsedEventArgs e) {
    // load and do stuff based on tick variable
}

private void start() {
    if (!timer.Enabled) {
        watch = Stopwatch.StartNew();
        timer.Start();
    }
}

Единственные решения, которые приходят мне на ум, — это какие-то хакерские решения, например, пересчет интервала в каждом цикле с помощью секундомера, чего я не хочу делать, потому что уверен, что есть решения получше.

Не могли бы вы попробовать использовать System.Threading.Timer вместо System.Timers.Timer и посмотреть, будет ли ситуация лучше?

Theodor Zoulias 30.05.2024 23:59

Кажется, приведенный выше код работает нормально для меня. Довел до 1000 тиков, и они все еще хорошо отслеживаются. Даже когда я добавил загруженную работу в calcManagerTick, это занимало ~ 50 мс. Редактировать: Это на .net 8.0. Каково ваше окружение? Кроме того, происходит ли это с вашим примером выше? Если нет, вам придется привести нам воспроизводимый пример.

NPras 31.05.2024 03:14

Таймеры .NET имеют точность около 15 мс, которая может постепенно увеличиваться (хотя задержка в 2 секунды после 15 секунд кажется немного экстремальной). Здесь вы можете найти возможное решение. Он Elapsed даже содержит свойство Delay, которое может сообщать о возможных поздних вызовах. Он также доступен в виде NuGet пакета.

György Kőszeg 31.05.2024 10:48

@NPras Кажется, это действительно была версия .net. После того, как вы сказали мне, что код не воспроизводит его, я почувствовал себя очень глупо, потому что я действительно не проверил дважды, действительно ли сокращенный код вызвал проблему (извините). Удвойте это смущением, когда код в новом проекте действительно не вызвал проблему. Добавьте к этому серьезную путаницу, когда проблема возникла в одном и том же коде исходного проекта. Не спрашивайте меня, почему, но мой проект был настроен для работы на .Net 4... В любом случае... в этом была проблема... Все еще чувствую себя немного глупо. Собираюсь создать ответ и связать вас с ним. Спасибо

Kingfinity 31.05.2024 20:21
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
93
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

@NPras помог, упомянув, что версия .Net может быть устаревшей. Я действительно работал на старой версии .Net 4. Или, по крайней мере, в файле конфигурации написано что-то вроде

<supportedRuntime version = "v4.0" sku = ".NETFramework,Version=v4.8" />

Возможно, это только базовая версия .Net. Я не знаю. Новый проект решил проблему. Если бы мне пришлось взять другой идентификатор запроса, скажем, я однажды выбрал «Приложение Windows Forms», а другой раз «Приложение Windows Forms (.Net Framework)»... какой бы ни была разница.

Да, MS плохо называет вещи. .Net Framework — их старая технология, предназначенная только для Windows. Он был переписан с нуля и открыт под названием .Net Core. Затем они отказались от «ядерной» части, и теперь это просто .Net.

NPras 03.06.2024 03:26

Другие вопросы по теме