【问题标题】:How to do something every millisecond or better on Windows如何在 Windows 上每毫秒或更好地做某事
【发布时间】:2011-10-23 16:48:05
【问题描述】:

这个问题不是关于在 Windows(XP 或更高版本)上准确计时,而是关于非常快速地通过回调或中断做某事。

我需要每隔 1 毫秒定期执行一次操作,最好是每 100 微秒执行一次。我需要做的是以这种速率驱动一些异步硬件(以太网)以向网络输出稳定的数据包流,并使该流看起来尽可能规则和同步。但如果问题可以与(以太网)设备分开,最好知道一般答案。

在您说“甚至不要考虑使用 Windows !!!”之前,先了解一下上下文。并非所有实时系统都有相同的需求。尽管需要平均每 10-16 毫秒左右处理一次音频或图像块,但大多数情况下,歌曲和视频在 Windows 上的播放是可以接受的。通过适当的缓冲,Windows 可以有其可变的延迟,但硬件可以在很大程度上不受它们的影响,并保持稳定的同步事件流发生。即便如此,我们大多数人都能容忍偶尔出现的故障。我的应用程序就是这样 - 可能相当宽容。

对我来说昂贵的选择是将我的整个应用程序移植到 Linux。但 Linux 只是在相同硬件上运行的不同软件,所以我强烈倾向于编写一些更好的软件,并坚持使用 Windows。我有幸消除所有竞争的硬件和软件(没有互联网或其他网络访问,没有其他应用程序运行等)。我是否有希望让 Windows 做到这一点?我会遇到什么限制?

我知道我的目标硬件有一个高性能事件定时器,并且这个定时器可以被编程为中断,但没有驱动程序。我可以写一个吗?那里有有用的例子吗?我还没有找到一个。这会干扰 QueryPerformanceCounter 吗?我将使用以太网设备这一事实是否意味着如果我明智地使用 select() 一切都会变得简单?

欢迎提供有用文章的指针 - 我发现了许多关于如何获得准确时间的概述,但除了使用相当于繁忙等待的情况外,还没有一个关于如何执行此类操作的概述。 有没有办法避免忙碌的等待?是否有内核模式或设备驱动选项?

【问题讨论】:

  • 您可以尝试使用答案之一中提到的多媒体计时器,但这也取决于硬件。 Windows(和 Linux)根本就不是实时操作系统,也不能以 1 毫秒的规模做事。如果您不想切换到 RTOS,您最好尝试找到可以接受来自 Windows 应用程序的大量数据包缓冲区的以太网硬件,并每隔 1ms 或 100us 滴答声将这些数据包一点一点地发送出去。
  • 非常好 - 对于我的具体情况,这听起来是一个非常有用的解决方案。关于什么以太网硬件可以做到这一点的任何提示?
  • 我什至不知道这样的事情是否存在,我只是在抛出一个想法 :-) 你应该能够从 TI、摩托罗拉、PIC 等公司购买具有以太网端口并运行可以执行此操作的 RTOS。然后,您必须通过以太网或 RS232 从您的 PC 与它通信以传输数据。此解决方案涉及更多内容,而不是简单地编写 PC 应用程序,或找到可以满足您需求的现成硬件,因此这取决于您愿意为此投入的时间和精力。跨度>
  • 更新 - 编辑问题以加粗强调。
  • 如果你有一个多核,你可以分配一个核来做计时(旋转一个高分辨率时间源)。有关详细信息,请参阅this

标签: c++ windows raw-ethernet


【解决方案1】:

您应该考虑查看多媒体计时器。这些计时器旨在满足您正在查看的分辨率。

看看这里MSDN

【讨论】:

  • 理论上,timeSetEvent() 提供了我想使用的那种回调机制。我应该担心它是一个过时的功能吗?我没有看到队列计时器是 MS 想要的替代品 - 请参阅 virtualdub.org/blog/pivot/entry.php?id=272
  • 仔细阅读,我发现回调函数在它可以进行的系统调用中是有限的。特别是它只能发送消息与另一个线程交谈。因此,如果您想要一个非常快的低级 IO 处理函数链接到计时器,并让另一个线程为其提供服务,那么它必须是一个缓慢的 UI 线程。要么就是这样,要么你抓住脉冲事件的机会,并希望你想要运行以响应事件的高优先级线程实际上运行时没有明显的延迟。因此,这并不完全是最引人注目的答案,但可能比将大型应用程序移植到新平台更好。
  • 抱歉 - 轻微错误 - 您可以设置或脉冲通过 timeSetEvent() 触发的事件。我读到的一条评论让我相信,由于 PulseEvent() 的已知问题,这些计时器功能已被弃用。可能是这样,也可能不是——我无法确认。尽管如此,当我读到它时,它要么在计时器线程中快速运行并且具有慢速服务器线程,要么将事件触发到快速线程并且具有不受限制的服务器线程,但是在线程调度上赌一把。我现在真的很想知道如何为高性能事件计时器编写驱动程序!
【解决方案2】:

我使用 DirectX 9 和 QueryPerformanceCounter 完成了这项工作,但您至少需要占用一个内核,因为任务切换会搞砸。

为了更好地比较 tierers,您可以查看

http://www.geisswerks.com/ryan/FAQS/timing.html

【讨论】:

  • 谢谢 - 那篇文章是我迄今为止发现的最好的文章,但最后的结论是等待最多 1 毫秒,并使用 100% 的 CPU 时间。如果我想要一个 800us 的周期,那听起来是个非常非常糟糕的主意!
  • 我的游戏循环一直在循环(100% cpu),因为我的音乐需要以正确的精确毫秒(1ms 分辨率)打开/关闭。它对用户不友好,但如果您让系统切换任务,您将迟到。这就是为什么我建议至少占用一个核心。你可以更快地解决,所以 800us 是可能的,顺便说一句。
  • 100% CPU 总是听起来像是寻求更好解决方案的理由,特别是因为我还有其他需要保持响应的实时线程,但谢谢。
【解决方案3】:

如果您遇到计时器粒度问题,我建议您使用带有自旋循环的老式 Sleep()。本质上,代码应该执行以下操作:

void PrecisionSleep(uint64 microSec)
{
    uint64 start_time;

    start_time = GetCurrentTime(); // assuming GetCurrentTime() returns microsecs

    // Calculate number of 10ms intervals using standard OS sleep.
    Sleep(10*(microSec/10000)); // assuming Sleep() takes millisecs as argument

    // Spin loop to spend the rest of the time in
    while(GetCurrentTime() - start_time < microSec)
    {}
}

这样,您将获得高精度睡眠,如果其中许多大于调度粒度(假设为 10 毫秒),则不会对您的 CPU 造成太大负担。您可以循环发送数据包,同时使用高精度睡眠对它们进行计时。

音频在大多数系统上都能正常工作的原因是音频设备有自己的时钟。您只需将音频数据缓冲到它,它会负责播放它并在缓冲区为空时中断程序。事实上,如果播放引擎依赖于 CPU 时钟,声卡时钟和 CPU 时钟之间的时间偏差可能会导致问题。

编辑: 您可以通过使用一个线程来进行计时器抽象,该线程使用一个受锁保护的定时条目最小堆(堆比较在到期时间戳上完成),然后您可以在 PrecisionSleep( ) 到下一个时间戳完成。

【讨论】:

  • 当然 Sleep() 是问题的一部分,而不是解决方案 - 正如geisswerks.com/ryan/FAQS/timing.html 所示,即使是标称 1ms 的 Sleep(1) 也往往会提供 10ms 的睡眠时间,或者如果您需要 3-4ms碰巧有一个旧的Vaio。这太不一致了。
  • 没错。我不得不在某个时候做这样的事情,我通过实验找出 Sleep() 粒度是什么,并在上面的算法中使用该值。
  • -1。主要是因为答案没有解决高分辨率计时器,需要 1ms 或更好。另外,我看不到它如何提高计时器的准确性,例如小持续时间。如果我没看错,那么您在要求的时间内正在睡觉 - 然后旋转。对于初学者来说,睡眠会在很短的时间内睡过头而不是睡过头(正如@omatai 正确指出的那样) - 然后旋转通常不是很好的做法,并且会占用处理器周期。如果我读错了,请告诉我。
  • 是的,睡过头是可能的。你应该在睡觉时考虑到这一点。正如我所说,大多数操作系统都有调度粒度。如果调度粒度是 10 毫秒,那么休眠 1 毫秒会在下一个调度周期(可以是 1 到 10 毫秒之间的任何时间)中唤醒线程。您需要找到一个上限(例如 10 毫秒)并在上述算法中使用它。如果你给 PrecisionSleep() 函数一个非常小的时间段,那么它会一起跳过 Sleep() 并直接进入忙等待状态。在使用细粒度计时器之前,请记住内核到用户的转换开销。
【解决方案4】:

在程序启动时使用NtSetTimerResolution 设置定时器分辨率。是的,它是未记录的功能,但效果很好。您也可以使用NtQueryTimerResolution 了解计时器分辨率(确保在设置新分辨率之前和之后)。

您需要使用GetProcAddressNTDLL.DLL 动态获取这些函数的地址,因为它没有在头文件或任何LIB 文件中声明。

以这种方式设置计时器分辨率会影响Sleep、Windows 计时器、返回当前时间的函数等。

【讨论】:

    猜你喜欢
    • 2013-09-27
    • 2014-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多