在我工作的公司中,我们制造的机器由运行在 Windows 操作系统上的软件控制。 C# 应用程序与总线 Controller 通信(通过 DLL)。总线 Controller 以 15 毫秒的节拍时间运行。这意味着,我们以 15 毫秒的心跳从总线 Controller ( 实时)获取系统中实际传感器的更新。
现在,这些机器正在进化到下一代,在那里我们得到了一个以 1 毫秒的速度运行的新总线 Controller 。由于每个人都意识到 Windows 不是实时操作系统,问题就出现了:我们是否应该将软件的控制部分转移到实时应用程序(在实时操作系统上,例如(软)PLC)。
如果我们留在 windows 平台上,我们无法保证响应能力。这本身不一定是问题;如果我们错过了几个总线周期(有一些小问题),机器的生产速度只会稍微慢一些(这是可以接受的)。
让我担心的部分是主机控制线程与我们从实时 Controller (每毫秒)收到的更新之间的线程同步。
我在哪里可以了解更多有关 Windows/.NET C# 在以毫秒为单位进行线程同步时的行为方式?我知道,例如Thread.Sleep(1)
最多可能需要 15 毫秒,因为 Windows 正在抢占其他任务,所以当我每毫秒使用 Monitor.PulseAll
在两个线程之间进行同步时,这会如何反射(reflect)?我可以期待同样的不可预知的行为吗?当我在 Windows 应用程序中进入 1ms 的软实时要求时,它是在自找麻烦吗?
我希望在线程的这些方面有经验的人可以阐明这一点。如果我需要澄清更多,一定要开枪。
最佳答案
您的场景听起来像是信息亭模式/专用应用程序的候选场景。
In the company I work for we build machines which are controlled by software running on Windows OS.
如果是这样,您可以装配机器,使您的低延迟 I/O 线程可以在线程和进程优先级最大化的专用核心上运行。此外,确保机器有足够的内核来处理缓冲线程以及处理传输中数据的任何其他线程。如果可能,缓冲区应预先分配内存以避免垃圾收集瓶颈。
@Aron 的示例适用于数据完整性可能在一定程度上受到损害的情况。在音频中,出于多种原因,延迟在录制过程中很重要,但对于纯播放,数据丢失在一定程度上是可以接受的。我假设这不是您的选择。
当然,Windows 并非设计为实时操作系统,但如果您将它用于专用应用程序,则您可以控制它的各个方面,并可以关闭所有不相关的服务和后台进程。
我已经相当成功地编写了软件,通过测量它们的功率补偿响应时间来监控 UPS 单元应对功率波动的能力(免责声明:但不用于商业目的)。由于每个样本要测量的数据非常小,因此 GC 没有问题,我们为缓冲区循环了预分配的内存块。
一些派上用场的微优化:
- 使用不可变的
结构
轮询 I/O 数据。 - 优化数据结构以更好地处理内存分配。
- 优化处理算法以最大限度地减少 CPU 缓存未命中。
- 使用优化的缓冲区类来保存传输中的数据。
- 使用
Monitor
和Interlocked
类进行同步。 - 将不安全代码与
(void*)
结合使用,以各种方式轻松访问缓冲区数组,从而缩短处理时间。最少使用Marshal
和Buffer.BlockCopy
。
最后,您可以采用 DDK 方式编写一个小型驱动程序。尽管题外话,DFMirage是视频驱动程序的一个很好的例子,它为不同的屏幕捕获提供基于事件和轮询模型,以便消费者应用程序可以根据系统负载即时选择。
至于 Thread.Sleep,考虑到您的能耗边界,您可以尽可能少地使用它。有了多余的进程,Thread.Sleep(1) 应该没有您想象的那么糟糕。尝试以下操作,看看您得到了什么。请注意,这已在 SO 编辑器中编码,因此我可能犯了错误。
Thread.CurrentThread.Priority = ThreadPriority.Highest;
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
var ticks = 0L;
var iteration = 0D;
var timer = new Stopwatch();
do
{
iteration++;
timer.Restart();
Thread.Sleep(1);
timer.Stop();
ticks += timer.Elapsed.Ticks;
if (Console.KeyAvailable) { if (Console.ReadKey(true).Key == ConsoleKey.Escape) { break; } }
Console.WriteLine("Elapsed (ms): Last Iteration = {0:N2}, Average = {1:N2}.", timer.Elapsed.TotalMilliseconds, TimeSpan.FromTicks((long) (ticks / iteration)).TotalMilliseconds);
}
while (true);
Console.WriteLine();
Console.WriteLine();
Console.Write("Press any key to continue...");
Console.ReadKey(true);
关于c# - Windows 操作系统中(软实时)计时要求的限制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24841387/