Получение точных тиков от таймера в С#
Я пытаюсь перестроить старое приложение метронома, которое изначально было написано с использованием MFC на С++, написанное на .NET с использованием С#. Одна из проблем, с которыми я сталкиваюсь, заключается в том, чтобы таймер был достаточно "тик".
Например, если принять простой BPM (удары в минуту) 120, таймер должен указывать каждые 0,5 секунды (или 500 миллисекунд). Однако использование этого в качестве основы для тиков не совсем точно, так как .NET гарантирует, что ваш таймер не будет отмечен до истечения прошедшего времени.
В настоящее время, чтобы обойти это для того же самого примера 120 BPM, который использовался выше, я устанавливаю тики примерно на 100 миллисекунд и воспроизвожу только звук щелчка на каждом пятом таймере. Это немного улучшает точность, но если вы чувствуете себя немного взломанным.
Итак, каков наилучший способ получить точные клещи? Я знаю, что есть больше таймеров, чем таймер окон, который легко доступен в Visual Studio, но я не очень-то знаком с ними.
Ответы
Ответ 1
В .NET есть три класса таймера под названием "Таймер". Похоже, что вы используете Windows Forms, но на самом деле вы можете найти класс System.Threading.Timer более полезным - но будьте осторожны, потому что он обращается к потоку пула, поэтому вы не можете напрямую взаимодействовать с вашей формой из обратный вызов.
Другим подходом может быть p/invoke для мультимедийных таймеров Win32 - timeGetTime, timeSetPeriod и т.д.
Быстрый Google нашел это, что может быть полезно http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx
"Мультимедиа" (таймер) - это ключевое слово для поиска в этом контексте.
Ответ 2
Что такое приложение на С++? Вы всегда можете использовать одну и ту же вещь или поместить код таймера из С++ в класс С++/CLI.
Ответ 3
У меня возникла эта проблема при разработке недавнего проекта регистрации данных. Проблема с таймерами .NET(windows.forms, system.threading и system.timer) заключается в том, что они являются точными вплоть до 10 миллисекунд, что связано с планированием событий, встроенным в .NET. Я полагаю. (Я говорю о .NET 2 здесь). Это было неприемлемо для меня, поэтому мне пришлось использовать мультимедийный таймер (вам нужно импортировать dll). Я также написал класс-оболочку для всех таймеров, и поэтому вы можете переключаться между ними, если это необходимо, используя минимальные изменения кода. Посмотрите мой блог здесь:
http://www.indigo79.net/archives/27
Ответ 4
Другая возможность заключается в том, что в WPF реализована ошибка DispatcherTimer
(существует несоответствие между миллисекундами и тиками, вызывающее потенциальную неточность в зависимости от точного времени выполнения процесса), как показано ниже:
http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/DispatcherTimer.cs,143
class DispatcherTimer
{
public TimeSpan Interval
{
set
{
...
_interval = value;
// Notice below bug: ticks1 + milliseconds [Bug1]
_dueTimeInTicks = Environment.TickCount + (int)_interval.TotalMilliseconds;
}
}
}
http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/Dispatcher.cs
class Dispatcher
{
private object UpdateWin32TimerFromDispatcherThread(object unused)
{
...
_dueTimeInTicks = timer._dueTimeInTicks;
SetWin32Timer(_dueTimeInTicks);
}
private void SetWin32Timer(int dueTimeInTicks)
{
...
// Notice below bug: (ticks1 + milliseconds) - ticks2 [Bug2 - almost cancels Bug1, delta is mostly milliseconds not ticks]
int delta = dueTimeInTicks - Environment.TickCount;
SafeNativeMethods.SetTimer(
new HandleRef(this, _window.Value.Handle),
TIMERID_TIMERS,
delta); // <-- [Bug3 - if delta is ticks, it should be divided by TimeSpan.TicksPerMillisecond = 10000]
}
}
http://referencesource.microsoft.com/#WindowsBase/Shared/MS/Win32/SafeNativeMethodsCLR.cs,505
class SafeNativeMethodsPrivate
{
...
[DllImport(ExternDll.User32, SetLastError = true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)]
public static extern IntPtr SetTimer(HandleRef hWnd, int nIDEvent, int uElapse, NativeMethods.TimerProc lpTimerFunc);
}
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644906%28v=vs.85%29.aspx
uElapse [in]
Type: UINT
The time-out value, in milliseconds. // <-- milliseconds were needed eventually
Ответ 5
Классы таймера могут начать странно вести себя, когда код события "tick" таймера не завершен, к тому моменту, когда произойдет следующий "тик". Один из способов борьбы с этим - отключить таймер в начале события тика, а затем снова включить его в конце.
Однако этот подход не подходит в случаях, когда время выполнения кода "tick" не является допустимой ошибкой в тактике галочки, поскольку таймер будет отключен (не считая) в течение этого времени.
Если отключить таймер является опцией, вы также можете добиться такого же эффекта, создав отдельный поток, который выполняется, засыпает за миллисекунды, выполняет, спит и т.д.
Ответ 6
System.Windows.Forms.Timer
ограничивается точностью 55 миллисекунд...