2008-08-12 5 views
14

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

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

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

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

ответ

9

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

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

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

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

1

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

0

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

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

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

+0

Но тогда вы можете быть уверены, что нить спит по крайней мере х millioseconds; планировщик потоков не подтверждает, что поток будет выполняться с точным количеством миллисекунд – Wilhelm 2009-07-18 22:32:28

+0

Вправо. Я согласен с тем, что вы сказали о невозможности определить время следующего тика. То, что я говорю, заключается в том, что вы не хотите, чтобы выполнялся код события tick с предыдущего тика, когда наступает следующий тик. – 2009-07-20 13:51:26

0

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

1

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

1

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