从C#中的计时器获取准确的刻度

时间:2008-08-12 12:20:30

标签: .net timer

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

例如,假设一个简单的BPM(每分钟节拍数)为120,计时器应每隔0.5秒(或500毫秒)打勾。然而,使用它作为刻度的基础并不完全准确,因为.NET只保证计时器在经过的时间过去之前不会打勾。

目前,为了解决上面使用的相同的120 BPM示例,我将刻度设置为100毫秒,并且仅在每个第5个计时器刻度上播放点击声。这确实提高了准确性,但如果感觉有点像黑客。

那么,获得准确滴答的最佳方法是什么?我知道有更多的计时器可用于Visual Studio中随时可用的Windows窗体计时器,但我并不熟悉它们。

6 个答案:

答案 0 :(得分:9)

.NET中有三个名为“Timer”的计时器类。听起来你正在使用Windows Forms,但实际上你可能会发现System.Threading.Timer类更有用 - 但要小心,因为它回调了池线程,所以你不能直接与你的表单交互回调。

另一种方法可能是p / invoke到Win32多媒体计时器 - timeGetTime,timeSetPeriod等。

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

'多媒体'(计时器)是在此上下文中搜索的热门词。

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

答案 3 :(得分: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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

当定时器'tick'事件代码在下一次'tick'发生时没有完成执行时,定时器类可以开始表现奇怪。解决此问题的一种方法是在tick事件开始时禁用计时器,然后在结束时重新启用它。

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

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