В моем текущем проекте я хочу отправить предопределенную тактильную информацию, синхронизированную с видео. Например, в видео сильный ветер, и я хочу передать сенсацию. Эти тактильные данные необходимо рассчитать.
Я хочу рассчитывать и отправлять данные с фиксированным интервалом 0,1 секунды. План состоит в том, чтобы отправить рассчитанные данные, а затем начать расчет следующих данных. Для этого я использовал System.Timers.Timer с AutoReset = true и истекшим событием.
Условия, которых я пытаюсь достичь, такие.
Однако я наткнулся на проблему с синхронизацией таймера. Вот журнал каждый раз, когда я отправляю 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();
}
}
Единственные решения, которые приходят мне на ум, — это какие-то хакерские решения, например, пересчет интервала в каждом цикле с помощью секундомера, чего я не хочу делать, потому что уверен, что есть решения получше.
Кажется, приведенный выше код работает нормально для меня. Довел до 1000 тиков, и они все еще хорошо отслеживаются. Даже когда я добавил загруженную работу в calcManagerTick, это занимало ~ 50 мс. Редактировать: Это на .net 8.0. Каково ваше окружение? Кроме того, происходит ли это с вашим примером выше? Если нет, вам придется привести нам воспроизводимый пример.
Таймеры .NET имеют точность около 15 мс, которая может постепенно увеличиваться (хотя задержка в 2 секунды после 15 секунд кажется немного экстремальной). Здесь вы можете найти возможное решение. Он Elapsed даже содержит свойство Delay, которое может сообщать о возможных поздних вызовах. Он также доступен в виде NuGet пакета.
@NPras Кажется, это действительно была версия .net. После того, как вы сказали мне, что код не воспроизводит его, я почувствовал себя очень глупо, потому что я действительно не проверил дважды, действительно ли сокращенный код вызвал проблему (извините). Удвойте это смущением, когда код в новом проекте действительно не вызвал проблему. Добавьте к этому серьезную путаницу, когда проблема возникла в одном и том же коде исходного проекта. Не спрашивайте меня, почему, но мой проект был настроен для работы на .Net 4... В любом случае... в этом была проблема... Все еще чувствую себя немного глупо. Собираюсь создать ответ и связать вас с ним. Спасибо





@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.
Не могли бы вы попробовать использовать System.Threading.Timer вместо
System.Timers.Timerи посмотреть, будет ли ситуация лучше?