当前位置:  开发笔记 > 编程语言 > 正文

C#为什么定时器频率极度偏离?

如何解决《C#为什么定时器频率极度偏离?》经验,为你挑选了2个好方法。

这两个System.Timers.TimerSystem.Threading.Timer火在离请求的那些相当不同的时间间隔.例如:

new System.Timers.Timer(1000d / 20);

产生一个每秒发射16次而不是20次的计时器.

为了确保太长的事件处理程序没有副作用,我写了这个小测试程序:

int[] frequencies = { 5, 10, 15, 20, 30, 50, 75, 100, 200, 500 };

// Test System.Timers.Timer
foreach (int frequency in frequencies)
{
    int count = 0;

    // Initialize timer
    System.Timers.Timer timer = new System.Timers.Timer(1000d / frequency);
    timer.Elapsed += delegate { Interlocked.Increment(ref count); };

    // Count for 10 seconds
    DateTime start = DateTime.Now;
    timer.Enabled = true;
    while (DateTime.Now < start + TimeSpan.FromSeconds(10))
        Thread.Sleep(10);
    timer.Enabled = false;

    // Calculate actual frequency
    Console.WriteLine(
        "Requested frequency: {0}\nActual frequency: {1}\n",
        frequency, count / 10d);
}

输出如下所示:

要求:5赫兹; 实际:4,8 Hz
请求:10 Hz; 实际:9,1 Hz
请求:15 Hz; 实际:12,7 Hz
请求:20 Hz; 实际:16 Hz
请求:30 Hz; 实际:21,3 Hz
请求:50 Hz; 实际:31,8 Hz
请求:75 Hz; 实际:63,9 Hz
请求:100 Hz; 实际:63,8 Hz
请求:200 Hz; 实际:63,9 Hz
请求:500 Hz; 实际:63,9赫兹

实际频率偏离所要求的频率高达36%.(显然不能超过64赫兹.)鉴于微软建议这个计时器"更准确" System.Windows.Forms.Timer,这让我很困惑.

顺便说一句,这些不是随机偏差.它们每次都是相同的值.另一个计时器类的类似测试程序System.Threading.Timer显示了完全相同的结果.

在我的实际程序中,我需要以每秒50个样本的速度收集测量结果.这不应该需要实时系统.每秒获得32个样本而不是50个样本非常令人沮丧.

有任何想法吗?

@Chris:你是对的,所有间隔似乎都是1/64秒左右的整数倍.顺便说一下,在事件处理程序中添加Thread.Sleep(...)没有任何区别.这有意义,因为它System.Threading.Timer使用线程池,因此每个事件都在一个空闲线程上触发.



1> Justin Tanne..:

如果你使用winmm.dll,你可以使用更多的CPU时间,但有更好的控制.

以下是修改为使用winmm.dll计时器的示例

const String WINMM = "winmm.dll";
const String KERNEL32 = "kernel32.dll";

delegate void MMTimerProc (UInt32 timerid, UInt32 msg, IntPtr user, UInt32 dw1, UInt32 dw2);

[DllImport(WINMM)]
static extern uint timeSetEvent(
      UInt32            uDelay,      
      UInt32            uResolution, 
      [MarshalAs(UnmanagedType.FunctionPtr)] MMTimerProc lpTimeProc,  
      UInt32            dwUser,      
      Int32             fuEvent      
    );

[DllImport(WINMM)]
static extern uint timeKillEvent(uint uTimerID);

// Library used for more accurate timing
[DllImport(KERNEL32)]
static extern bool QueryPerformanceCounter(out long PerformanceCount);
[DllImport(KERNEL32)]
static extern bool QueryPerformanceFrequency(out long Frequency);

static long CPUFrequency;

static int count;

static void Main(string[] args)
{            
    QueryPerformanceFrequency(out CPUFrequency);

    int[] frequencies = { 5, 10, 15, 20, 30, 50, 75, 100, 200, 500 };

    foreach (int freq in frequencies)
    {
        count = 0;

        long start = GetTimestamp();

        // start timer
        uint timerId = timeSetEvent((uint)(1000 / freq), 0, new MMTimerProc(TimerFunction), 0, 1);

        // wait 10 seconds
        while (DeltaMilliseconds(start, GetTimestamp()) < 10000)
        {
            Thread.Sleep(1);
        }

        // end timer
        timeKillEvent(timerId);

        Console.WriteLine("Requested frequency: {0}\nActual frequency: {1}\n", freq, count / 10);
    }

    Console.ReadLine();
}

static void TimerFunction(UInt32 timerid, UInt32 msg, IntPtr user, UInt32 dw1, UInt32 dw2)
{
    Interlocked.Increment(ref count);
}

static public long DeltaMilliseconds(long earlyTimestamp, long lateTimestamp)
{
    return (((lateTimestamp - earlyTimestamp) * 1000) / CPUFrequency);
}

static public long GetTimestamp()
{
    long result;
    QueryPerformanceCounter(out result);
    return result;
}

这是我得到的输出:

Requested frequency: 5
Actual frequency: 5

Requested frequency: 10
Actual frequency: 10

Requested frequency: 15
Actual frequency: 15

Requested frequency: 20
Actual frequency: 19

Requested frequency: 30
Actual frequency: 30

Requested frequency: 50
Actual frequency: 50

Requested frequency: 75
Actual frequency: 76

Requested frequency: 100
Actual frequency: 100

Requested frequency: 200
Actual frequency: 200

Requested frequency: 500
Actual frequency: 500

希望这可以帮助.



2> Mitch Denny..:

这些类不适合实时使用,并且受Windows等操作系统的动态调度特性的限制.如果您需要实时执行,您可能想要查看一些嵌入式硬件.我不是100%肯定,但我认为.netcpu可能是芯片上较小的.NET运行时的实时版本.

http://www.arm.com/markets/emerging_applications/armpp/8070.html

当然 - 您需要评估这些间隔的准确性是多么重要,因为附加到它们的代码将在非实时操作系统上执行.当然,除非这是一个纯粹的学术问题(在这种情况下 - 是的,它很有趣!:P).

推荐阅读
贴进你的心聆听你的世界
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有