Получение точных тиков из таймера в C #

Я пытаюсь перестроить старое приложение с метрономом, которое изначально было написано с использованием MFC на C ++, для написания на .NET с использованием C #. Одна из проблем, с которыми я сталкиваюсь, заключается в том, чтобы заставить таймер "тикать" достаточно точно.

Например, предполагая, что легкий BPM (ударов в минуту) равен 120, таймер должен срабатывать каждые 0,5 секунды (или 500 миллисекунд). Использование этого в качестве основы для тиков, однако, не совсем точно, так как .NET только гарантирует, что ваш таймер не будет тикать, пока истекшее время не прошло.

В настоящее время, чтобы обойти это для того же примера 120 ударов в минуту, который использовался выше, я устанавливаю тики примерно на 100 миллисекунд и играю только звук щелчка на каждом 5-м тактовом тике. Это немного повышает точность, но если вы чувствуете себя немного взломанным.

Итак, каков наилучший способ получить точные тики? Я знаю, что в Visual Studio доступно больше таймеров, чем таймера форм Windows, но я не очень знаком с ними.

12.08.2008 12:20:29
6 ОТВЕТОВ
РЕШЕНИЕ

В .NET есть три класса таймеров, которые называются «Таймер». Похоже, что вы используете Windows Forms, но на самом деле вы можете найти класс System.Threading.Timer более полезным - но будьте осторожны, потому что он вызывает обратный поток в пуле, поэтому вы не можете напрямую взаимодействовать с вашей формой из обратный звонок.

Другой подход может заключаться в том, чтобы вызывать / вызывать мультимедийные таймеры Win32 - timeGetTime, timeSetPeriod и т. Д.

Быстрый Google нашел это, что может быть полезно http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx

«Мультимедиа» (таймер) - модное слово для поиска в этом контексте.

10
12.08.2008 12:34:43

Что использует приложение C ++? Вы всегда можете использовать одно и то же или обернуть код таймера из C ++ в класс C ++ / CLI.

1
12.08.2008 13:17:49

Классы таймера могут начать странно себя вести, когда код события «отметка» таймера не завершится к тому времени, когда произойдет следующая отметка. Одним из способов борьбы с этим является отключение таймера в начале события тика, а затем его повторное включение в конце.

Однако этот подход не подходит в тех случаях, когда время выполнения кода «тика» не является приемлемой ошибкой во времени тика, поскольку таймер будет отключен (не считая) в течение этого времени.

Если отключение таймера является опцией, то вы также можете достичь того же эффекта, создав отдельный поток, который выполняется, спит в течение x миллисекунд, выполняет, спит и т. Д.

0
12.08.2008 14:09:28
Но тогда вы можете быть уверены, что поток спит не менее x миллисекунд; планировщик потока не удостоверяет, что поток будет работать с точным счетом millesond
Wilhelm 18.07.2009 22:32:28
Правильно. Я согласен с тем, что вы сказали о невозможности точно определить время следующего тика. Суть того, что я говорю, заключается в том, что вы не хотите выполнять код события тика из предыдущего тика, когда происходит ваш следующий тик.
Brad Barker 20.07.2009 13:51:26

System.Windows.Forms.Timer ограничен с точностью до 55 миллисекунд ...

0
4.01.2012 14:19:05
Есть ли на это официальная документация?
MajesticRa 23.04.2011 15:52:06
@MajesticRa: msdn.microsoft.com/en-us/library/… См. Текст в желтом поле
Anthony Sottile 23.01.2012 01:30:42
Спасибо! Когда я написал это, я упустил момент, что yazanpro означает именно таймер форм. И думал, что он говорит о таймерах вообще. Но большое спасибо за ваш ответ!
MajesticRa 23.01.2012 02:15:38

У меня была эта проблема при разработке недавнего проекта регистрации данных. Проблема с таймерами .NET (windows.forms, system.threading и system.timer) заключается в том, что они точны только до 10 миллисекунд, что связано с планированием событий, встроенным в .NET, как я полагаю. (Я говорю о .NET 2 здесь). Это было неприемлемо для меня, и поэтому мне пришлось использовать мультимедийный таймер (вам нужно импортировать DLL). Я также написал класс-оболочку для всех таймеров, чтобы вы могли переключаться между ними при необходимости, используя минимальные изменения кода. Проверьте мой блог здесь: http://www.indigo79.net/archives/27

1
11.02.2011 10:09:44

Другая возможность состоит в том, что существует ошибка в реализации DispatcherTimer в WPF (существует несоответствие между миллисекундами и тиками, вызывающее потенциальную неточность в зависимости от точного времени выполнения процесса), как показано ниже:

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
1
18.12.2014 14:26:56