实现实时的1毫秒准确事件,而无需进行线程调度 [英] Achieving realtime 1 millisecond accurate events without suffering from thread scheduling

查看:143
本文介绍了实现实时的1毫秒准确事件,而无需进行线程调度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题

Problem

我正在使用 .Net 4.5 创建一个基于Windows 7的C#WPF 应用程序,它的主要功能之一是调用与自定义硬件接口的某些功能和一组用户定义的周期时间.例如,用户可能选择每10或20毫秒调用两个函数,而每500毫秒调用另一个函数. 用户可以选择的最小循环时间为1毫秒.

I am creating a Windows 7 based C# WPF application using .Net 4.5, and one its major features is to call certain functions that interface with custom hardware with a set of user defined cycle times. For example the user might choose two functions to be called every 10 or 20 milliseconds and another every 500 milliseconds. The smallest cycle time the user can choose is 1 milliseconds.

起初,计时似乎是准确的,并且根据需要每1毫秒调用一次函数.但是我们后来注意到,大约 1-2%的计时不准确,有些功能被称为延迟仅5毫秒,而其他功能则可能延迟达100毫秒.即使周期时间大于1毫秒,我们仍然面临这样的问题:线程在应该调用外部函数的时候就处于睡眠状态(一个20毫秒的函数可能会被延迟50毫秒,因为线程处于休眠状态而没有调用该函数)

At first it seemed that the timings were accurate and the functions were called every 1 millisecond as required. But we later noticed that about 1-2% of the timings were not accurate, were some functions were called just 5 milliseconds late, and others could reach up to 100 milliseconds late. Even with cycle times greater than 1 msec, we faced the problem that the thread slept at the time it should have called the external function (a 20 msecs function could be called 50 msecs late because the thread was sleeping and didnt call the function)

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

After analysis we concluded that these delays were sporadic, with no noticeable pattern, and that the main possible reason behind these delays were OS scheduling and thread context switching, in other words our thread wasn't awake all the time like we need it to be.

由于Windows 7不是RTOS,因此我们需要确定是否可以以某种方式解决此问题.但是我们确实知道这个问题在Windows上是可以解决的,因为我们使用了具有类似功能的其他工具,这些工具可以满足那些时序限制,最大容错度为0.7毫秒.

As windows 7 is not an RTOS, we need to find if we can work around this problem somehow. But we do know for sure that this problem is fixable on windows, as we use other tools with similar functionality that can meet those timing constraints with a maximum of 0.7 ms error tolerance.

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

Our application is multithreaded with about a maximum of 30 threads running at the same, its current peak CPU usage is about 13%

尝试的解决方案

Attempted Solutions

我们尝试了许多不同的方法,计时主要是使用秒表计时器测量的,而 IsHighResolution 是正确的(使用了其他计时器,但我们没有注意到很大的差异):

We tried many different things, timing was mainly measured using the stopwatch timer and IsHighResolution was true (other timers were used but we did not notice much difference):

  1. 创建一个单独的线程并赋予其较高的优先级
    结果:无效(同时使用可怕的Thread.Sleep(),没有可怕的Thread.Sleep()并使用连续轮询)

  1. Creating a separate thread and giving it high priority
    Result: Ineffective (using both the terrible Thread.Sleep(), and without it and using continuous polling)

使用C#任务(线程池)
结果:几乎没有改善

Using a C# task (Thread pool)
Result: very little improvement

使用周期为1ms的多媒体计时器
结果:无效或更糟,多媒体定时器可以准确地唤醒操作系统,但是操作系统可以选择运行另一个线程(不能保证1毫秒),但是即使这样,延迟有时也会大得多

Using a multimedia timer with 1ms periodicity
Result: Ineffective or worse, multimedia timers are accurate at waking up the OS, but the OS may choose to run another thread, no 1ms guarantee, but even then, delays could be much bigger occasionally

创建了一个单独的独立C#项目,该项目仅包含while循环和秒表计时器
结果:在大多数情况下,准确性甚至是(以微秒为单位),但但偶尔线程会休眠

Created a separate standalone C# project that just contained a while loop and stopwatch timer
Result: most of the time the accuracy was great even in microseconds, but occasionally the thread sleeps

重复点4,但将进程优先级设置为实时/高"
结果:很好的数字,几乎没有一条消息有明显的延迟.

Repeated point 4, but set the process priority to Realtime/High
Result: Very good numbers, almost not a single message had significant delay.

结论:

Conclusion:

从上一章中我们发现我们有5种可能的行动方案,但是我们需要在此类问题上有丰富经验的专家为我们指出正确的方向:

From the previous we found that we had 5 possible courses of action, but we need someone knowledgeable with experience in such problems to point us in the right direction:

  1. 我们的工具可以进行优化,并以某种方式管理线程,以确保1ms的实时要求.优化的一部分可能是将工具的过程优先级设置为高"或实时",但这似乎不是明智的决定,因为用户可能同时使用其他几种工具.

  1. Our tool can be optimized and the threads managed somehow to insure the 1ms realtime requirement. maybe part of the optimization is setting the process priority of the tool to high or Realtime, but that does not seem like a wise decisions, as users could be using several other tools at the same time.

我们将工具分为两个过程,一个包含GUI和所有非时间紧迫的操作,另一个包含最小量的时间紧迫的操作并将其设置为高/实时优先级,然后使用IPC(如WCF)之间进行通信.这可以通过两种方式使我们受益

We divide our tool into two processes, one that contains the GUI And all the non time critical operations, and the other containing the minimal amount of time critical operations and set it to high/real time priority, and use IPC (like WCF) to communication between the processes. This could benefit us in two ways

  1. 由于发生的操作少得多,因此其他进程的饥饿可能性降低.

  1. Less probability of starvation for other processes as much less operations are happening.

该进程的线程数较少,因此(很少或没有)线程休眠的可能性

The process would have less threads so (much less or no) probability of thread sleeping

注意:接下来的两点将涉及内核空间,请注意,我对内核空间和编写驱动程序的了解很少,因此我可能会对其使用方式做出一些错误的假设.

Note: The next two points will deal with kernel space, please note that I have little information about kernel space and writing drivers, so I might be making some wrong assumptions about how it could be used.

  1. 在内核空间中创建一个驱动程序,该驱动程序每1毫秒使用一次较低级别的中断来触发一个事件,该事件迫使线程在进程中执行其指定任务.

  1. Creating a driver in kernel space that uses lower level interrupts every 1ms to fire an event that forces the thread to perform its designated task in the process.

将时间紧迫的组件移至内核空间,可以通过API和回调与程序主体进行任何接口.

Moving the time critical components to kernel space, any interfacing with the Main body of the programs could be done through APIs and callbacks.

也许所有这些都是无效的,我们可能需要使用Windows RTOS扩展,例如IntervalZero RTOS平台?

Perhaps all of these are not valid, and we might need to use a windows RTOS extension like IntervalZero RTOS platform?

问题本身

The Question Itself

我正在寻找两个答案,希望它们有良好的消息来源.

There are two answers I am looking for, and I hope they are backed with good sources.

  1. 这真的是线程和上下文切换问题吗?还是我们一直都在错过某些东西?

  1. Is this truly a threading and context switching problem? Or have we been missing something all of this time?

保证5个选项中的哪个可以解决此问题,如果有多个选项,哪个最简单?如果这些选项都不能解决问题,那又能解决什么呢?请记住,我们使用基准测试的其他工具确实可以在Windows上达到所需的计时精度,并且当CPU处于重负载时,100,000个中的一两个计时可能会少于2毫秒,这是可以接受的.

Which of the 5 options is guaranteed to fix this problem, and if several are, which is the easiest? If none of these options can fix it, what can? Please remember that other tools we have bench-marked do indeed reach the required timing accuracy on windows, and when the CPU is under heavy load, one or two timings out of 100,000 could be off by less than 2 milliseconds, which is very acceptable.

推荐答案

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

这取决于您要达到的精度.如果您的目标是+/- 1毫秒,那么您有合理的机会在3)到5)的情况下完成任务.要点1)和2)的结合是可行的方法:

This depends on what accuracy your are trying to achieve. If you're aiming for say +/- 1ms, you have a reasonable chance to get it done without points 3) to 5). The combination of points 1) and 2) is the way to go:

  • 将您的代码分为时间紧迫的部分和时间紧迫的部分(GUI等),然后将它们放入单独的进程中.让它们通过像样的IPC(管道,共享内存等)进行通信.
  • 提高时间优先级进程的进程优先级类和线程优先级.不幸的是,c# ThreadPriority枚举仅允许作为最大优先级.因此,您必须查看 Process :: PriorityClass属性允许访问REALTIME_PRIORITY_CLASS (24).注意:以这种优先级运行的代码将阻止所有其他代码.您必须使用很少的计算量来编写代码,并且非常是安全的.
  • 使用 ProcessThread :: ProcessorAffinity 属性以调整正确的核心使用情况.提示:您可能希望使时间紧迫的线程远离CPU_0(属性值0x0001),因为Windows内核更喜欢使用此CPU进行特定操作.示例:在具有4个逻辑处理器的平台上,您可以将ProcessoreAffinity属性指定为0x000E,以排除CPU_0.
  • 系统计时器分辨率通常由其他应用程序设置.因此,只有当您指定系统计时器分辨率时,它才是可预测的.某些应用程序/驱动程序甚至将计时器分辨率设置为0.5ms. 这可能超出您的设置范围,并可能导致应用程序出现故障. 有关如何将计时器分辨率设置为0.5ms的信息,请参见 SO答案. (注意:此决议的支持取决于平台.)
  • Split your code into time critical parts and less time critical parts (GUI etc.) and put them into separate processes. Let them comunicate by means of decent IPC (pipes, shared memory, and alike).
  • Raise the process priority class and the thread priority of the time critical process. Unfortunately, the c# ThreadPriority Enumeration only permits THREAD_PRIORITY_HIGHEST(2) as the maximimum priority. Therefore you'd have to look into the SetThreadPriority function which allows access to THREAD_PRIORITY_TIME_CRITICAL (15). The Process::PriorityClass Property allows to access REALTIME_PRIORITY_CLASS (24). Note: Code running at such priorities will push all other code out of the way. You'd have to make the code with very littly computation and very safe.
  • Use the ProcessThread::ProcessorAffinity property to adjust proper core usage. Hint: you may want to keep your time critical threads away from CPU_0 (property value 0x0001) because the Windows kernel prefers this CPU for specific operations. Example: On a platform with 4 logical processors you'd specifiy the ProcessoreAffinity property with 0x000E to exclude CPU_0.
  • The systems timer resolution is often set by other applications. Therefore, it is only predictable when you dictate the systems timer resolution. Some applications/drivers even set the timer resolution to 0.5ms. This may be beyond youre setting and can lead to hiccups in you application. See this SO answer on how to set the timer resolution to 0.5ms. (Note: The support of this resolution is platform dependent.)

一般说明:全部取决于负载.尽管Windows不是实时操作系统",但Windows仍然可以很好地运行.但是,实时系统也依赖于低负载.没有任何保证,即使在RT-OS负载很重的情况下也无法保证.

General remarks: All depends on load. Windows can do pretty well despite the fact that it is not a "realtime OS". However, also realtime systems rely on low load. Nothing is guaranteed, not even on an RT-OS when it is heavily loaded.

这篇关于实现实时的1毫秒准确事件,而无需进行线程调度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆