我怎样才能(合理地)每N毫秒精确地执行一次动作?

时间:2010-03-25 17:09:13

标签: c++-cli precision time-precision timestamping

我有一台机器使用NTP客户端同步到互联网时间,因此它的系统时钟应该相当准确。

我有一个我正在开发的应用程序,它实时记录数据,处理它然后传递它。我现在要做的是输出数据每N毫秒与系统时钟对齐。所以,例如,如果我想做20毫秒的间隔,我的外观应该是这样的:

13:15:05:000
13:15:05:020
13:15:05:040
13:15:05:060

我已经看到了使用秒表类的建议,但这只是测量时间跨度而不是寻找特定的时间戳。执行此操作的代码在其自己的线程中运行,因此如果我需要执行一些相对阻塞的调用,则应该是一个问题。

非常感激地接受任何有关如何达到合理(接近或优于1ms精度)的建议。

8 个答案:

答案 0 :(得分:2)

不知道它与C ++ / CLR有多好,但你可能想看看multimedia timers
Windows并不是真正实时的,但这是最接近的

答案 1 :(得分:2)

当缩短时间段时,您可以从timeGetTime()中获得非常准确的时间戳。您只需要做一些工作就可以将其返回值转换为时钟时间。此示例C#代码显示了该方法:

using System;
using System.Runtime.InteropServices;

class Program {
    static void Main(string[] args) {
        timeBeginPeriod(1);
        uint tick0 = timeGetTime();
        var startDate = DateTime.Now;
        uint tick1 = tick0;
        for (int ix = 0; ix < 20; ++ix) {
            uint tick2 = 0;
            do {  // Burn 20 msec
                tick2 = timeGetTime();
            } while (tick2 - tick1 < 20);
            var currDate = startDate.Add(new TimeSpan((tick2 - tick0) * 10000));
            Console.WriteLine(currDate.ToString("HH:mm:ss:ffff"));
            tick1 = tick2;
        }
        timeEndPeriod(1);
        Console.ReadLine();
    }
    [DllImport("winmm.dll")]
    private static extern int timeBeginPeriod(int period);
    [DllImport("winmm.dll")]
    private static extern int timeEndPeriod(int period);
    [DllImport("winmm.dll")]
    private static extern uint timeGetTime();
}

第二个想法,这只是测量。要定期执行操作,您必须使用timeSetEvent()。只要使用timeBeginPeriod(),就可以使回调期非常接近1毫秒。一个很好的例子是,当前一次回调因任何原因延迟时,它会自动补偿。

答案 2 :(得分:1)

您最好的选择是使用内联汇编并将此代码块写为设备驱动程序。

那样:

  • 您可以控制指令数
  • 您的申请将具有执行优先权

答案 3 :(得分:1)

最终,您不能保证您想要的是什么,因为操作系统必须尊重来自其他进程的请求才能运行,这意味着在您希望进程运行的那一刻,其他东西总是很忙。但是你可以使用timeBeginPeriod来改善问题,使你的过程更有可能及时切换到你的过程,并且可能在你如何在迭代之间等待 - 例如。大多数但不是所有时间都在睡觉,然后使用忙碌循环。

答案 4 :(得分:1)

尝试在两个线程中执行此操作。在一个线程中,使用this之类的东西来查询循环中的高精度计时器。当您检测到与20ms边界对齐(或合理接近)的时间戳时,请向日志输出线程发送信号以及要使用的时间戳。您的日志输出线程将只是等待一个信号,然后获取传入的时间戳并输出所需的任何内容。将两者保持在不同的线程中将确保您的日志输出线程不会干扰定时器(这实质上是模拟硬件定时器中断,这将是我在嵌入式平台上执行此操作的方式)。

答案 5 :(得分:0)

CreateWaitableTimer / SetWaitableTimer和高优先级线程应该精确到大约1ms。我不知道为什么示例输出中的毫秒字段有四位数,最大值为999(因为1000毫秒= 1秒)。

答案 6 :(得分:0)

因为如你所说,这不一定是完美的,有一些事情可以做。

据我所知,没有与特定时间同步的计时器。因此,您必须计算下次的时间并安排特定时间的计时器。如果您的计时器只有delta支持,那么这很容易计算,但会增加更多的错误,因为在计算delta和计时器进入内核的时间之间很容易从CPU中启动。

正如已经指出的,Windows不是实时操作系统。所以你必须假设即使你安排一个计时器在“:0010”下车,你的代码甚至可能在那之后很久才执行(例如,“:0540”)。只要你妥善处理这些问题,事情就会“好”。

答案 7 :(得分:0)

20ms大约是Windows上时间片的长度。如果没有像银泰这样的RT添加,就无法在Windows中可靠地达到1ms的时序。在Windows中,我认为您的选项是WaitForSingleObject,SleepEx和繁忙的循环。