【问题标题】:How do I run a function with precise timing?如何以精确的时间运行函数?
【发布时间】:2011-11-01 20:44:08
【问题描述】:

我需要一个函数在 +/- 1 毫秒内的精确时间运行。我尝试了以下方法,但最终执行之间的最短时间为 15 毫秒。

void Main()
{
    System.Timers.Timer timer = new System.Timers.Timer(1);   // executes every 15ms
    timer.Elapsed += new System.Timers.ElapsedEventHandler(myFunction);

    System.Timers.Timer timer2 = new System.Timers.Timer(5);   // executes every 15ms
    timer2.Elapsed += new System.Timers.ElapsedEventHandler(myFunction);

    System.Timers.Timer timer2 = new System.Timers.Timer(20);   // executes every 31ms
    timer3.Elapsed += new System.Timers.ElapsedEventHandler(myFunction);

    timer.Start();
    timer2.Start();
    timer3.Start();

}

void myFunction()
{
    doWord();
}

使用 Thread.Sleep() 获得相同的结果。

申请概要。

我将读取一个包含 1553 条消息(每条都带有时间戳)的文件。我需要以文件包含的尽可能接近的时间重播这些消息。消息的时间戳以微秒为单位记录,但我只需要毫秒精度。

这是使用 DDC 1553 卡(PCI 卡)完成的。我有一个分析器,它允许我查看消息,包括消息之间的增量时间,以衡量我的准确性。

我使用的机器有一个带超线程的 QuadCore。使用 for(int i=0; .....) 我可以得到 0.5 毫秒的精度。然而,这是非常低效的,如果可能的话,宁愿使用更现实、更便携的方法。

【问题讨论】:

  • 那是一个非常狭窄的时间窗口,你想每毫秒做什么?请记住,Timer 事件通常由 ThreadPool 引发,因此它们必须从池中获取线程并引发事件,这需要时间。
  • 仅供参考:切勿将Thread.Sleep 用于时间敏感的任务。 IIRC,它保证线程将睡眠时间至少,但是它可以睡眠的最长时间是未定义的。有关更多信息,请参阅this 问题。抱歉,对于您的实际问题,我无法提供更多帮助。
  • 定时器分辨率为 1/64 秒,15.625 毫秒。 Pinvoking timeBeginPeriod 和 timeSetEvent 可以获得一个 1 毫秒的计时器。不要假设您实际上会持续获得 1 毫秒。
  • @James:我正在使用 DDC 1553 卡进行 1553 异步消息传递。所以我需要能够在 1553 +/- 1 毫秒内发送这些消息。

标签: c# .net multithreading performance timer


【解决方案1】:

.NET、C#,甚至一般的 Windows 都不是适合非常精细计时的实时操作系统。

最糟糕的选择包括使用Timer 类和Thread.Sleep()

您可以使用 Stopwatch 类相当准确地测量时间,但就准确地等待设定的时间过去而言.. 没有内置机制。

如果您可以准确地勾勒出您想要做什么,假设它不是运动控制、硬件接口等,那么可能有比依赖非常精确的计时器更好的解决方案。

更新; Neal:如果您以对时间敏感的方式与硬件交互,您应该使用不同的解决方案。您可以使用Stopwatch 进行紧密循环,但只要您这样做,它就会占用大量 CPU。而且它可能不够准确。例如:PIC 芯片、FPGA、I/O 卡或接口,基本上是其他任何东西。

【讨论】:

  • 我添加了我正在尝试做的事情的简要概要。
  • 我已经添加了更新。不要在 Windows 上的软件中这样做!
  • 我直接与相应计算机中的 PCI 卡连接。正如您在更新中建议的那样,我可以使用循环获得非常准确的结果,但是,我只在测试环境中尝试过这个,所以我不确定它的可靠性。没有别的选择,只能使用窗户。该卡的 API 是一个 Windows API。我认为 DDC 也有一个 Linux API,但是,当我必须将该项目合并到基于 Windows 的 GUI 项目中时,它可能会开始导致问题。
  • 恐怕这仍然是答案。如果你需要做这样的事情,你必须依靠 Windows 以外的其他东西来做计时。大多数体面的硬件 IO 都有缓冲区或自己的可编程输出;您可以在硬件上创建一个循环或定时,或者给它一个 IO / 定时信息缓冲区。它的症结仍然是,您无法在 Windows 中按照您想要的方式进行操作。将时间委托给其他地方。
  • 我最终使用了一个单独的线程,该线程在 100% 的时间内使用基本循环来延迟。然而,这并不是一个好的解决方案,我缺乏选择。
【解决方案2】:

您可以使用高分辨率计时器,但它依赖于设备。你必须查询它。有关说明,请参阅此 MSDN 页面:http://msdn.microsoft.com/en-us/library/aa964692%28v=vs.80%29.aspx

但是System.Diagnostics.Stopwatch 应该可以为您提供接近 1 毫秒的精度。

【讨论】:

  • 秒表只记录事件,就像真正的秒表一样。您不能在间隔内触发事件。
  • 是的,但他可以在单独的线程上有一个循环或其他东西,并检查是否已经过了 1ms,然后触发他的方法。
  • 这将是非常低效的,上下文切换和线程调度程序粒度很可能意味着时间也不准确。
  • 同意。也许还有另一种方法可以用完全不同的方法实现他的目标。但是如果不知道上下文就很难判断。
【解决方案3】:

您可以使用具有合理准确性的System.Threading.Timer。请记住,它不会发布到 UI 线程上,因此您需要正确委派任何 UI 交互。

您也可以使用多媒体计时器来执行此操作,它具有非常高分辨率的计时功能。见http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx

【讨论】:

  • 这是我要添加的答案。 System.Threading.Timer 的分辨率高于 System.Windows.Forms.Timer。另一种方法是 P/Invoke 多媒体计时器 (msdn.microsoft.com/en-us/library/dd743609(v=VS.85).aspx),专为高分辨率计时事件而设计。或者使用 C# 的众多包装器之一。
  • System.Threading.Timer 显然与 System.Timers.Timer 具有相同的精度。所以这没有帮助。
  • 我建议 P/Invoking 多媒体计时器。它具有非常高的精度。如果您正在处理硬件,即使是几百纳秒的时间差异也会很大。线程的时间片比这大得多,所以在 C# 中你可能很难做到这一点。您可能需要考虑编写一个通过中断处理此类事件的本机驱动程序 - 这将产生近乎即时的响应时间。
【解决方案4】:

我不知道如何在现代版本的 Windows 中做这样的事情 - 只是偶然发现了这个老问题,并回忆起我在很久以前遇到过类似的问题......

回到石器时代(Win 3.11 和更高版本的 Win95),我能够获得非常高且可重复的实时性能(10kHz 没问题,抖动也相当不错 - 在 90MHz Pentium 上的微秒范围内)通过重新编程实时中断并连接到定时器的不可屏蔽中断。当时这涉及到能够直接访问计时器的 VxD(虚拟设备驱动程序)。还需要在汇编中获得专用的共享内存空间和代码(可能将汇编与 C/C++ 混合使用——显然,频率越高,循环需要越紧)。

基本上,我将计时器的周期减少到我想要的时间,然后执行我的代码 - 它会定期回调操作系统,以便操作系统体验它所期望的间隔。还需要挂钩操作系统用于调整间隔的函数并相应地调整我的回调(假设我的代码始终以高于操作系统所需的频率运行)。实际上用它通过打印机端口进行运动控制。从未将其发布到已发布的软件中,但确实获得了基本的台式 CNC 控制器。

代码在 Win98 中停止运行,我再也没有尝试过。访问硬件变得更加复杂,并且几乎总是以某种方式“虚拟化”。

在尝试在专用 RTOS 环境之外获得某种类型的 RTOS 性能时,我会首先查看设备驱动程序编程和可能的游戏(例如 Direct X)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-22
    • 1970-01-01
    相关资源
    最近更新 更多