对于计时器精度 - 我应该使用句号还是设置新的计时器?

时间:2016-05-03 10:45:15

标签: c# timing

我需要精确地做一些事情。

目前,我正在使用:

int delayMS = (60 - DateTime.Now.Second) * 1000;

BarTimer = new System.Threading.Timer(new TimerCallback(BarEndProcess), null, delayMS, 0);

这可能比每分钟重启定时器更精确,延迟时间如下:

<style type="text/css">
  div.inline { float:left; }
  .clearBoth { clear:both; }
</style>

我希望CallBack尽可能接近分钟边界。

1 个答案:

答案 0 :(得分:1)

看起来你需要在每次发射时重新同步你的计时器,以防止经过的时间间隔漂移。

这是我编写的一个测试程序,用于查看计时器回调是否会漂移。此示例尝试每10秒进行一次回调:

using System;
using System.Diagnostics;
using System.Threading;

namespace Demo
{
    static class Program
    {
        static void Main()
        {
            var timer = new Timer(test, null, 10000, 10000);
            Console.ReadLine();
            GC.KeepAlive(timer);
        }

        static void test(object obj)
        {
            if (stopwatch == null)
                stopwatch = Stopwatch.StartNew();

            Console.WriteLine(stopwatch.ElapsedMilliseconds);
        }

        static Stopwatch stopwatch;
    }
}

这个结果(在午餐时间运行!)是:

0
9995
19996
29997
39997
49998
59998
69999
80000
90000
100001
110002
120002
130003
140003
150004
160004
170005
180006
190007
200008
210009
220009
230010
240011
250012
260013
270014
280014
...
2800158
2810158
2820159
2830160
2840160
2850161
2860161
2870162
2880163
2890163
2900164
2910165
2920166
2930166
2940167
2950168

正如您所看到的,回调发生的时间逐渐远离每10秒漂移。

您可以通过测量绝对经过时间并调整下一个刻度的回调时间来解决此问题。 Stopwatch应该适合测量已用时间,但您也可以使用DateTime.Now来执行此操作。

这是一个使用Stopwatch重新校准计时器的示例程序。这个例子试图每10秒钟进行一次回调。

using System;
using System.Diagnostics;
using System.Threading;

namespace Demo
{
    static class Program
    {
        const int PERIOD_MILLISECONDS = 10000;

        static void Main()
        {
            timer = new Timer(test, null, PERIOD_MILLISECONDS, PERIOD_MILLISECONDS);
            Console.ReadLine();
            GC.KeepAlive(timer);
        }

        static void test(object dummy)
        {
            if (stopwatch == null)
                stopwatch = Stopwatch.StartNew();

            Console.WriteLine(stopwatch.ElapsedMilliseconds);

            long newPeriod = PERIOD_MILLISECONDS - stopwatch.ElapsedMilliseconds + expectedElapsedMillseconds;
            timer.Change(newPeriod, PERIOD_MILLISECONDS);

            expectedElapsedMillseconds += PERIOD_MILLISECONDS;
        }

        static Stopwatch stopwatch;
        static long      expectedElapsedMillseconds;
        static Timer     timer;
    }
}

此输出如下所示:

0
10010
20014
30011
40013
50009
60016
70001
80000
90001
100000
110000
120000
130001
140001
150001
160000
170001
180001
190001
200001
210001
220001
230001
240001
250001
260001
270001
280000
290000
300001
310000
320000
330001
340001
350001
360001

开始时有轻微的摆动,但请注意回调时间总是在它应该发生的16毫秒内,并且它没有漂移。