【发布时间】:2016-12-02 18:11:36
【问题描述】:
问题
我正在使用 .Net 4.5 创建一个基于 Windows 7 的 C# WPF 应用程序,其主要功能之一是调用与自定义硬件接口的某些函数 具有一组用户定义的循环时间。例如,用户可以选择每 10 或 20 毫秒调用两个函数,每 500 毫秒调用另一个函数。 用户可以选择的最小循环时间为 1 毫秒。
起初,时间似乎是准确的,并且根据需要每 1 毫秒调用一次函数。但我们后来注意到,大约有 1-2% 的计时不准确,有些函数被调用时仅延迟了 5 毫秒,而其他函数可能延迟了 100 毫秒。即使循环时间大于 1 毫秒,我们也面临线程在它应该调用外部函数的时间休眠的问题(20 毫秒的函数可能会延迟 50 毫秒,因为线程正在休眠并且没有调用该函数)
经过分析,我们得出结论,这些延迟是零星的,没有明显的模式,并且这些延迟背后的主要原因可能是操作系统调度和线程上下文切换,换句话说,我们的线程并没有像我们需要的那样一直处于唤醒状态它是。
由于 Windows 7 不是 RTOS,我们需要确定是否可以通过某种方式解决此问题。但我们确实知道这个问题在 Windows 上是可以解决的,因为我们使用具有类似功能的其他工具可以满足最大 0.7 毫秒容错的时间限制。
我们的应用程序是多线程的,最多同时运行大约 30 个线程,其当前的峰值 CPU 使用率约为 13%
尝试的解决方案
我们尝试了很多不同的方法,主要是使用 秒表计时器 来测量计时,并且 IsHighResolution 是正确的(使用了其他计时器,但我们没有发现太大差异):
创建一个单独的线程并为其赋予高优先级
结果:无效(使用可怕的Thread.Sleep(),没有它并使用连续轮询)使用 C# 任务(线程池)
结果:几乎没有改善使用具有 1 毫秒周期的多媒体计时器
结果:无效或更糟,多媒体定时器在唤醒操作系统时是准确的,但操作系统可能会选择运行另一个线程,没有 1ms 保证,但即便如此,有时延迟可能会更大创建了一个单独的独立 C# 项目,其中仅包含一个 while 循环和秒表计时器
结果:在大多数情况下,准确度非常好,甚至以微秒为单位,但偶尔线程会休眠重复第 4 点,但将进程优先级设置为实时/高
结果:非常好的数字,几乎没有一条消息有明显的延迟。
结论:
从前面我们发现我们有 5 种可能的行动方案,但我们需要在这些问题上具有丰富经验的人来为我们指明正确的方向:
我们的工具可以进行优化,并以某种方式管理线程以确保 1 毫秒的实时要求。也许优化的一部分是将工具的进程优先级设置为高或实时,但这似乎不是一个明智的决定,因为用户可能同时使用多个其他工具。
-
我们将我们的工具分为两个进程,一个包含 GUI 和所有非时间关键操作,另一个包含最少量的时间关键操作并将其设置为高/实时优先级,并使用 IPC(如WCF) 用于进程之间的通信。这可以从两个方面使我们受益
由于发生的操作更少,其他进程的饥饿概率也更低。
进程将拥有更少的线程,因此(更少或没有)线程休眠的可能性
注意:接下来的两点将涉及内核空间,请注意我对内核空间和编写驱动程序的信息很少,因此我可能对如何使用它做出了一些错误的假设。
在内核空间中创建一个驱动程序,该驱动程序每 1 毫秒使用一次较低级别的中断来触发一个事件,强制线程在进程中执行其指定的任务。
将时间关键组件移至内核空间,与程序主体的任何接口都可以通过 API 和回调完成。
也许所有这些都无效,我们可能需要使用 Windows RTOS 扩展,如 IntervalZero RTOS 平台?
问题本身
我正在寻找两个答案,我希望它们得到良好资源的支持。
这真的是线程和上下文切换问题吗?还是我们一直以来都遗漏了什么?
5 个选项中的哪一个可以保证解决这个问题,如果有几个,哪个是最简单的?如果这些选项都不能解决它,那有什么可以解决的呢?请记住,我们基准测试过的其他工具在 windows 上确实达到了所需的计时精度,并且当 CPU 处于重负载下时,100,000 次中的一到两次可能会关闭不到 2 毫秒,这是非常可以接受的。
【问题讨论】:
-
你认为哪个代码有问题?单独线程中的紧密循环 (
while true) 应始终以尽可能少的切换运行。 -
你需要一个实时操作系统...
-
@PatrickHofman 但这不是实际发生的情况,它有时会被关闭,这不是正常行为吗?
-
@MatthewWatson 但其他人已经做到了。
-
您可以查看MMCSS,它为多媒体等时间敏感代码提供了一些保证。但正如其他人所说,没有实时操作系统就无法获得 100% 保证的时间片。
标签: c# windows multithreading rtos windows-kernel