gpt4 book ai didi

c# - 实现实时 1 毫秒精确事件而不受线程调度的影响

转载 作者:可可西里 更新时间:2023-11-01 13:53:18 24 4
gpt4 key购买 nike

问题

我正在创建一个 基于 Windows 7 的 C# WPF 应用程序使用 .Net 4.5 ,其主要特点之一是调用某些与自定义硬件接口(interface)的函数 具有一组用户定义的循环时间。例如,用户可能会选择每 10 或 20 毫秒调用两个函数以及每 500 毫秒调用另一个函数。 用户可选择的最小循环时间为 1 毫秒。

起初,时间似乎是准确的,并且根据需要每 1 毫秒调用一次函数。但是我们后来注意到关于 1-2% 的时间不准确,有些函数被调用仅延迟 5 毫秒,而其他函数可能延迟达到 100 毫秒。即使循环时间大于 1 毫秒,我们也面临线程休眠的问题,它应该调用外部函数(一个 20 毫秒的函数可能会延迟 50 毫秒调用,因为线程正在休眠并且没有调用该函数)

经过分析,我们得出结论,这些延迟是零星的,没有明显的模式,这些延迟背后的主要原因可能是操作系统调度和线程上下文切换,换句话说,我们的线程并没有像我们需要的那样一直处于唤醒状态.

由于 Windows 7 不是 RTOS,我们需要找出是否可以以某种方式解决此问题。但我们确实知道这个问题在 Windows 上是可以解决的,因为我们使用其他具有类似功能的工具,可以满足最大 0.7 ms 容错的时序约束。

我们的应用程序是多线程的,最多同时运行 30 个线程,其当前 CPU 使用率峰值约为 13%

尝试的解决方案

我们尝试了很多不同的东西,时间主要是使用 来测量的。秒表计时器 ​​ IsHighResolution 是真的(使用了其他计时器,但我们没有注意到太大差异):

  • 创建一个单独的线程并赋予它高优先级
    结果:无效 (使用可怕的 Thread.Sleep() ,没有它并使用连续轮询)
  • 使用 C# 任务(线程池)
    结果:改进很小
  • 使用具有 1ms 周期的多媒体定时器
    结果:无效或更糟 , 多媒体定时器在唤醒操作系统时是准确的,但操作系统可能会选择运行另一个线程,没有 1ms 的保证,但即便如此,有时延迟可能会更大
  • 创建了一个单独的独立 C# 项目,其中仅包含一个 while 循环和秒表计时器
    结果:大部分时间甚至准确性也很好以微秒为单位,但偶尔线程会休眠
  • 重复第 4 点,但将进程优先级设置为实时/高
    结果:很好的号码 ,几乎没有一条消息有明显的延迟。

  • 结论:

    从前面我们发现我们有 5 种可能的行动方案,但我们需要一位对此类问题有经验的人来为我们指明正确的方向:
  • 我们的工具可以进行优化,并且以某种方式管理线程以确保 1 毫秒的实时要求。也许优化的一部分是将工具的进程优先级设置为高或实时,但这似乎不是一个明智的决定,因为用户可能同时使用其他几个工具。
  • 我们将我们的工具分为两个进程,一个包含 GUI 和所有非时间关键操作,另一个包含最少量的时间关键操作并将其设置为高/实时优先级,并使用 IPC(如 WCF)进程之间的通信。这可以通过两种方式使我们受益
  • 由于发生的操作更少,其他进程饥饿的可能性更小。
  • 该进程将拥有更少的线程,因此(更少或没有)线程休眠的可能性

  • 注意:接下来的两点将涉及内核空间,请注意我对内核空间和编写驱动程序的信息很少,所以我可能对如何使用它做出了一些错误的假设。
  • 在内核空间中创建一个驱动程序,它每 1 毫秒使用一次低级中断来触发一个事件,该事件强制线程在进程中执行其指定的任务。
  • 将时间关键组件移至内核空间,与程序主体的任何接口(interface)都可以通过 API 和回调完成。
  • 也许所有这些都无效,我们可能需要使用像 IntervalZero RTOS 平台这样的 Windows RTOS 扩展?

  • 问题本身

    我正在寻找两个答案,我希望它们有好的来源支持。
  • 这真的是线程和上下文切换问题吗?还是我们一直以来都错过了什么?
  • 5 个选项中的哪一个可以保证解决此问题,如果有几个选项,哪个最简单?如果这些选项都不能解决问题,有什么可以解决的?请记住,我们测试过的其他工具确实在windows上确实达到了所需的计时精度,并且在CPU负载较重的情况下,100,000中的一两个计时可能会关闭不到2毫秒,这是非常可以接受的。
  • 最佳答案

    5 个选项中的哪一个可以保证解决此问题?

    这取决于您要达到的准确度。如果您的目标是 +/- 1 毫秒,那么您有合理的机会在没有第 3) 到 5) 点的情况下完成它。点 1) 和 2) 的组合是要走的路:

  • 将您的代码拆分为时间关键部分和时间关键部分(GUI 等),并将它们放入单独的进程中。让它们通过合适的 IPC(管道、共享内存等)进行通信。
  • 提高进程优先级类和时间关键进程的线程优先级。不幸的是,c# ThreadPriority Enumeration只允许 THREAD_PRIORITY_HIGHEST(2)作为最大优先级。因此,您必须查看 SetThreadPriority允许访问 THREAD_PRIORITY_TIME_CRITICAL (15) 的函数. Process::PriorityClass Property允许访问 REALTIME_PRIORITY_CLASS (24) .注意:以这种优先级运行的代码会将所有其他代码推开。你必须用很少的计算和 来制作代码非常安全的。
  • 使用 ProcessThread::ProcessorAffinity属性来调整适当的核心使用。提示:您可能希望让时间关键线程远离 CPU_0(属性值 0x0001),因为 Windows 内核更喜欢该 CPU 进行特定操作。示例:在具有 4 个逻辑处理器的平台上,您可以使用 0x000E 指定 ProcessoreAffinity 属性以排除 CPU_0。
  • 系统计时器分辨率通常由其他应用程序设置。因此,只有当您指定系统计时器分辨率时,它才是可预测的。一些应用程序/驱动程序甚至将计时器分辨率设置为 0.5 毫秒。
    这可能超出您的设置,并可能导致您的应用程序出现问题。
    this所以回答如何将计时器分辨率设置为 0.5ms。 (注意:此分辨率的支持取决于平台。)

  • 一般备注:一切都取决于负载。尽管 Windows 不是“实时操作系统”,但它可以做得很好。然而,实时系统也依赖于低负载。什么都不能保证,即使在负载很重的 RT-OS 上也是如此。

    关于c# - 实现实时 1 毫秒精确事件而不受线程调度的影响,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38634880/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com