处理极小的时间增量

Dealing with extremely small increments of time

好吧,这个标题可能含糊不清,但请允许我解释一下。

我正在处理一个大列表,其中有数百条消息要作为字节数组发送到 CAN 总线。每条消息都有一个间隔 属性,详细说明必须以毫秒为单位发送消息的频率。但我会回到那个。

所以我有一个线程。线程循环遍历这个巨大的消息列表直到停止,body 大致如下:

Stopwatch timer = new Stopwatch();
sw.Start();
while(!ShouldStop)
{
   foreach(Message msg in list)
   {
      if(msg.IsReadyToSend(timer)) msg.Send();
   }
}

这非常有效,在遵守消息 objects' 间隔方面具有惊人的准确性。但是,它占用了整个 CPU。问题在于,由于消息的数量巨大以及 CAN 总线的性质,线程必须发送另一条消息之前通常只有不到半毫秒的时间。永远不会出现线程能够休眠超过 15 毫秒的情况。

我想弄清楚的是,是否有一种方法可以让线程暂时阻塞或让步,让处理器休眠并节省一些周期。如果我尝试将工作拆分为每条消息的线程,我是否会得到任何类型的准确性?还有其他我没有看到的方法吗?

编辑:可能值得一提的是消息的间隔 属性 不是绝对的。只要线程继续发送消息,接收方就应该很高兴,但是如果线程经常休眠,比如说,25 毫秒,因为更高优先级的线程窃取了它的 time-slice,它可能会为接收方发出危险信号。

如您所见,在 CPU 上 "wait" 的最准确方法是轮询 RTC。然而,这是计算密集型的。如果您需要在计时中获得时钟精度,没有其他方法。

但是,在你原来的post中,你说的时间是15ms的数量级。

在我家里的 3.3GHz 四核 i5 上,15ms x 3.3GHz = 5000 万个时钟周期(如果算上所有内核,则为 2 亿个)。

那是永恒。

对于您的目的来说,宽松的睡眠时间很可能已经足够准确了。

坦率地说,如果您需要 Hard RT,C# on the .net VM 运行 on the .net GC on the Windows Kernel 是错误的选择。

根据更新后的要求,使用 Sleep(0) 的默认设置很有可能就足够了 - 消息可能会以小批量的形式发送,但听起来还可以。使用多媒体计时器可能会使突发事件不那么引人注意。对消息的接收者建立更多的容忍度可能是更好的方法(如果可能)。


如果您需要具有良好保证的硬毫秒精度 - Windows 上的 C# 不是最佳选择 - 可能需要单独的硬件(甚至是 Adruino),或者至少需要比 C# 更低级别的代码。

Windows 不是 RT OS,所以你无法真正获得亚毫秒精度。

如果您需要亚毫秒精度,那么您所拥有的繁忙循环(可能在高优先级线程上)是常用方法。

您可以尝试使用多媒体计时器(示例 - Multimedia timer interrupts in C# (first two interrupts are bad)), as well to change default time slice to 1ms (see Why are .NET timers limited to 15 ms resolution? for sample/explanation)。

在任何情况下,您都应该意识到,如果有其他更高优先级的线程要调度,您的代码可能会丢失其时间片,并且您所有的努力都会白费。

注意:您显然应该考虑更合理的数据结构是否更合适(即堆或优先级队列可能更适合查找下一项)。