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

StackOverflow https://stackoverflow.com/questions/8742

  •  08-06-2019
  •  | 
  •  

Вопрос

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

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

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

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

Это было полезно?

Решение

В .NET есть три класса таймеров, называемых "Timer".Звучит так, как будто вы используете Windows Forms one, но на самом деле вы можете найти систему.Многопоточность.Класс Timer более полезен - но будьте осторожны, потому что он выполняет обратный вызов в потоке пула, поэтому вы не можете напрямую взаимодействовать с вашей формой из обратного вызова.

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

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

"Мультимедиа" (timer) - это ключевое слово для поиска в данном контексте.

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

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

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

Другая возможность заключается в том, что существует ошибка в реализации 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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top