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

发布于 2024-08-26 18:11:57 字数 367 浏览 5 评论 0原文

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

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

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

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

任何关于如何合理地实现这一目标(接近或优于 1 毫秒的精度就很好)的建议将非常感激。

I have a machine which uses an NTP client to sync up to internet time so it's system clock should be fairly accurate.

I've got an application which I'm developing which logs data in real time, processes it and then passes it on. What I'd like to do now is output that data every N milliseconds aligned with the system clock. So for example if I wanted to do 20ms intervals, my oututs ought to be something like this:

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

I've seen suggestions for using the stopwatch class, but that only measures time spans as opposed to looking for specific time stamps. The code to do this is running in it's own thread, so should be a problem if I need to do some relatively blocking calls.

Any suggestions on how to achieve this to a reasonable (close to or better than 1ms precision would be nice) would be very gratefully received.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

笨死的猪 2024-09-02 18:11:57

不知道它与 C++/CLR 的配合如何,但您可能想看看 多媒体计时器
Windows 并不是真正的实时,但这已经是最接近的了

Don't know how well it plays with C++/CLR but you probably want to look at multimedia timers,
Windows isn't really real-time but this is as close as it gets

顾北清歌寒 2024-09-02 18:11:57

当您减少时间段时,您可以从 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 毫秒的回调周期。一个好处是,当上一个回调因任何原因延迟时,它会自动进行补偿。

You can get a pretty accurate time stamp out of timeGetTime() when you reduce the time period. You'll just need some work to get its return value converted to a clock time. This sample C# code shows the approach:

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();
}

On second thought, this is just measurement. To get an action performed periodically, you'll have to use timeSetEvent(). As long as you use timeBeginPeriod(), you can get the callback period pretty close to 1 msec. One nicety is that it will automatically compensate when the previous callback was late for any reason.

渡你暖光 2024-09-02 18:11:57

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

这样:

  • 您可以控制指令计数
  • 您的应用程序将具有执行优先级

Your best bet is using inline assembly and writing this chunk of code as a device driver.

That way:

  • You have control over instruction count
  • Your application will have execution priority
绝不放开 2024-09-02 18:11:57

最终,您无法保证您想要什么,因为操作系统必须满足其他进程运行的请求,这意味着在您希望进程运行的同时,其他进程可能总是很忙。但是您可以使用 timeBeginPeriod 来改进问题,使您的流程更有可能及时切换到,并且可能巧妙地处理迭代之间的等待方式 - 例如。大部分时间(但不是全部时间)都在睡觉,然后在剩下的时间里使用忙循环。

Ultimately you can't guarantee what you want because the operating system has to honour requests from other processes to run, meaning that something else can always be busy at exactly the moment that you want your process to be running. But you can improve matters using timeBeginPeriod to make it more likely that your process can be switched to in a timely manner, and perhaps being cunning with how you wait between iterations - eg. sleeping for most but not all of the time and then using a busy-loop for the remainder.

想念有你 2024-09-02 18:11:57

尝试在两个线程中执行此操作。在一个线程中,使用类似 this 的内容循环查询高精度定时器。当您检测到与 20 毫秒边界对齐(或相当接近)的时间戳时,请将信号连同要使用的时间戳一起发送到日志输出线程。您的日志输出线程将简单地等待信号,然后获取传入的时间戳并输出所需的任何内容。将两个线程保持在单独的线程中将确保您的日志输出线程不会干扰计时器(这本质上是模拟硬件计时器中断,这就是我在嵌入式平台上执行此操作的方式)。

Try doing this in two threads. In one thread, use something like this to query a high-precision timer in a loop. When you detect a timestamp that aligns to (or is reasonably close to) a 20ms boundary, send a signal to your log output thread along with the timestamp to use. Your log output thread would simply wait for a signal, then grab the passed-in timestamp and output whatever is needed. Keeping the two in separate threads will make sure that your log output thread doesn't interfere with the timer (this is essentially emulating a hardware timer interrupt, which would be the way I would do it on an embedded platform).

慢慢从新开始 2024-09-02 18:11:57

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

CreateWaitableTimer/SetWaitableTimer and a high-priority thread should be accurate to about 1ms. I don't know why the millisecond field in your example output has four digits, the max value is 999 (since 1000 ms = 1 second).

迟月 2024-09-02 18:11:57

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

据我所知,不存在与特定时间同步的计时器。因此,您必须计算下一次时间并安排该特定时间的计时器。如果你的定时器只有增量支持,那么很容易计算,但会增加更多错误,因为在计算增量的时间和定时器进入内核的时间之间,你很容易被踢出CPU。

正如已经指出的,Windows 不是实时操作系统。因此,您必须假设,即使您安排计时器在“:0010”停止,您的代码也可能直到该时间之后很久才执行(例如,“:0540”)。只要你妥善处理这些问题,一切都会“好”。

Since as you said, this doesn't have to be perfect, there are some thing that can be done.

As far as I know, there doesn't exist a timer that syncs with a specific time. So you will have to compute your next time and schedule the timer for that specific time. If your timer only has delta support, then that is easily computed but adds more error since the you could easily be kicked off the CPU between the time you compute your delta and the time the timer is entered into the kernel.

As already pointed out, Windows is not a real time OS. So you must assume that even if you schedule a timer to got off at ":0010", your code might not even execute until well after that time (for example, ":0540"). As long as you properly handle those issues, things will be "ok".

或十年 2024-09-02 18:11:57

20ms 大约是 Windows 上一个时间片的长度。如果没有像 Intime 这样的 RT 插件,就没有办法在 Windows 中可靠地达到 1ms 的计时。在 Windows 中,我认为您的选择是 WaitForSingleObject、SleepEx 和繁忙循环。

20ms is approximately the length of a time slice on Windows. There is no way to hit 1ms kind of timings in windows reliably without some sort of RT add on like Intime. In windows proper I think your options are WaitForSingleObject, SleepEx, and a busy loop.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文