【问题标题】:Getting accurate ticks from a timer in C#从 C# 中的计时器获取准确的刻度
【发布时间】:2010-09-05 17:49:52
【问题描述】:

我正在尝试重建一个旧的节拍器应用程序,该应用程序最初是使用 C++ 中的 MFC 编写的,然后使用 C# 在 .NET 中编写。我遇到的问题之一是让计时器足够准确地“滴答”。

例如,假设简单的 BPM(每分钟节拍数)为 120,则计时器应每 0.5 秒(或 500 毫秒)计时一次。但是,将其用作滴答的基础并不完全准确,因为 .NET 仅保证您的计时器在经过的时间过去之前不会滴答。

目前,为了解决上面使用的相同 120 BPM 示例的问题,我将滴答声设置为 100 毫秒,并且仅在每 5 个计时器滴答声时播放点击声音。这确实提高了相当多的准确性,但感觉有点像 hack。

那么,获得准确刻度的最佳方法是什么?我知道可用的计时器比 Visual Studio 中现成的 Windows 窗体计时器要多,但我对它们并不十分熟悉。

【问题讨论】:

    标签: .net timer


    【解决方案1】:

    另一种可能是 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
    

    【讨论】:

      【解决方案2】:

      System.Windows.Forms.Timer 的精度限制为 55 毫秒...

      【讨论】:

      • 有官方文档吗?
      • @MajesticRa: msdn.microsoft.com/en-us/library/… 见黄色框内的文字
      • 谢谢!当我写它时,我错过了 yazanpro 的意思就是 Forms 计时器这一点。并认为他在谈论一般的计时器。不过非常感谢您的回复!
      【解决方案3】:

      我在开发最近的数据记录项目时遇到了这个问题。 .NET 计时器(windows.forms、system.threading 和 system.timer)的问题在于,我相信它们只能精确到 10 毫秒左右,这是由于 .NET 中内置的事件调度。 (我在这里谈论.NET 2)。这对我来说是不可接受的,所以我不得不使用多媒体计时器(你需要导入 dll)。我还为所有计时器编写了一个包装类,因此您可以在必要时使用最少的代码更改在它们之间切换。在这里查看我的博客文章: http://www.indigo79.net/archives/27

      【讨论】:

        【解决方案4】:

        如果在下一个“滴答”发生时计时器“滴答”事件代码尚未完成执行,则计时器类可能会开始出现奇怪的行为。解决此问题的一种方法是在滴答事件开始时禁用计时器,然后在结束时重新启用它。

        但是,这种方法不适用于“滴答”代码的执行时间在滴答计时中是不可接受的错误的情况,因为在此期间计时器将被禁用(不计数)。

        如果禁用计时器是一个选项,那么您也可以通过创建一个单独的线程来实现相同的效果,该线程执行、休眠 x 毫秒、执行、休眠等......

        【讨论】:

        • 但是你只能确保线程至少休眠 x 毫秒;线程调度程序不保证线程将以精确的千分之一计数运行
        • 对。我同意你所说的无法确定下一个滴答声的时间。我所说的重点是,当您的下一个滴答发生时,您不希望从上一个滴答执行滴答事件代码。
        【解决方案5】:

        C++ 应用程序使用什么?您始终可以使用相同的东西或将 C++ 中的计时器代码包装到 C++/CLI 类中。

        【讨论】:

          【解决方案6】:

          .NET 中有三个称为“Timer”的计时器类。听起来您使用的是 Windows 窗体,但实际上您可能会发现 System.Threading.Timer 类更有用 - 但要小心,因为它会回调池线程,因此您无法直接与窗体交互回调。

          另一种方法可能是 p/调用 Win32 多媒体计时器 - timeGetTime、timeSetPeriod 等。

          一个快速的谷歌发现了这个,这可能很有用http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx

          “多媒体”(计时器)是在此上下文中搜索的流行词。

          【讨论】:

            猜你喜欢
            • 2010-09-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-03-19
            • 1970-01-01
            • 1970-01-01
            • 2010-11-27
            相关资源
            最近更新 更多