【问题标题】:Dealing with extremely small increments of time处理极小的时间增量
【发布时间】:2016-02-01 04:34:45
【问题描述】:

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

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

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

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

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

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

编辑:值得一提的是,Message 的 Interval 属性不是绝对的。只要线程继续发送消息,接收者就应该很高兴,但如果线程由于更高优先级的线程窃取其时间片而定期休眠,例如 25 毫秒,它可能会为接收者发出危险信号。

【问题讨论】:

  • 只需在while() 内添加一个Thread.Sleep(0) 即可使其播放不错。如果这是一个 GUI 应用程序,那么你的 while() 不应该在主线程上
  • @Micky Sleep(0) 将释放剩余的时间片 - 因此线程可能被安排在大约 7-10 毫秒(平均)内执行,因此最多发送 20 条消息(如" 不到半毫秒...发送另一条消息")
  • @AlexeiLevenkov 听起来是时候打破 Arduino 和分线板了! xkcd.com/730
  • 基于更新后的常规 Sleep(0) 可能是可以接受的。试试看。
  • 是的,经过考虑,我想我愿意放弃几毫秒的准确性损失。 Sleep(0) 应该可以解决我的问题。

标签: c# multithreading can-bus


【解决方案1】:

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


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

Windows 不是 RT 操作系统,因此您无法真正获得亚毫秒级的精度。

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

您可以尝试使用多媒体计时器(示例 - Multimedia timer interrupts in C# (first two interrupts are bad)),也可以将默认时间片更改为 1 毫秒(示例/说明请参见 Why are .NET timers limited to 15 ms resolution?)。

在任何情况下,您都应该意识到,如果要安排其他更高优先级的线程,您的代码可能会失去其时间片,而您的所有努力都将付诸东流。

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

【讨论】:

  • 重新排列数据结构肯定在我的“待办事项”清单上,但我想首先解决当前的问题,因为更多的 CPU 周期被浪费在旋转而不是计算消息是否需要将被寄出。无论如何,我会调查一个优先队列。
【解决方案2】:

正如您所发现的,在 CPU 上“等待”的最准确方法是轮询 RTC。然而,这是计算密集型的。如果您需要在计时上达到时钟精度,没有其他办法。

但是,在您原来的帖子中,您说时间是 15 毫秒。

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

那是永恒。

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

坦率地说,如果您需要 Hard RT,那么在 Windows 内核上的 .net GC 上运行的 .net VM 上的 C# 是错误的选择。

【讨论】:

  • 所以你会为我的线程的每个循环建议一个简单的 Thread.Sleep(0) ,或者至少,当我遍历整个消息列表并且不需要发送任何内容时?编辑:此外,线程循环之间的时间更像是 0.25 毫秒。我的意思是说线程永远不会空闲 15 毫秒。哎呀,5不太可能。
  • @user2008803 在阅读有关广达的信息后,是的。但这并不能保证它可以正常工作。我真正的建议是将工作加载到硬 RT 上,比如 Arduino,并用 C++ 等 RT 语言编写真正的 CAN 接口。
  • 我想我不得不称之为“足够接近”。准确性的损失在这里并不是真正的交易破坏者。这段代码并不意味着真的是一个真正的 CAN 接口,它更像是一个伪造的盒子,用于通过发送与服务器在真实工作环境中发送的相同消息来测试客户端代码。
猜你喜欢
  • 1970-01-01
  • 2015-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多