0

OK, that title was perhaps vague, but allow me to explain.

I'm dealing with a large list, of hundreds of messages to be sent to a CAN bus as byte arrays. Each of these messages has an Interval property detailing how often the message must be sent, in milliseconds. But I'll get back to that.

So I have a thread. The thread loops through this giant list of messages until stopped, with the body roughly like this:

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

This works great, with phenomenal accuracy in honoring the Message objects' Interval. However, it hogs an entire CPU. The problem is that, because of the massive number of messages and the nature of the CAN bus, there is generally less than half a millisecond before the thread has to send another message. There would never be a case the thread would be able to sleep for, say, more than 15 milliseconds.

What I'm trying to figure out is if there is a way to do this that allows for the thread to block or yield momentarily, allowing the processor to sleep and save some cycles. Would I get any kind of accuracy at all if I try splitting the work into a thread per message? Is there any other way of doing this that I'm not seeing?

EDIT: It may be worth mentioning that the Message's Interval property is not absolute. As long as the thread continues to spew messages, the receiver should be happy, but if the thread regularly sleeps for, say, 25 ms because of higher priority threads stealing its time-slice, it could raise red flags for the receiver.

  • Just add a `Thread.Sleep(0)` just inside the `while()` to make it play _nice_. If this is a GUI app, then your `while()` should not be on the main thread –  Feb 01 '16 at 04:44
  • @Micky Sleep(0) will release the rest of time-slice - and hence thread likely be scheduled for execution in about 7-10ms (on average), hence missing sending up to 20 messages (as " less than half a millisecond before ... send another message") – Alexei Levenkov Feb 01 '16 at 04:50
  • 1
    @AlexeiLevenkov sounds like its time to break out the Arduino and breakout boards! https://xkcd.com/730/ – Aron Feb 01 '16 at 05:01
  • Based on updated post regular Sleep(0) may be acceptable. Try. – Alexei Levenkov Feb 01 '16 at 05:16
  • Yes, after considering, I think I'm willing to part with a few milliseconds of lost accuracy. Sleep(0) should solve my problem. – user2008803 Feb 01 '16 at 05:18
  • @AlexeiLevenkov I did not know that it was that long, thanks buddy. –  Feb 01 '16 at 06:22

2 Answers2

3

Based on the updated requirement there is very good chance that default setup with Sleep(0) could be enough - messages may be sent in small bursts, but it sounds like is ok. Using multimedia timer may make burst less noticeable. Building more tolerance to receiver of the messages may be better approach (if possible).


If you need hard milliseconds accuracy with good guarantees - C# on Windows is not the best choice - separate hardware (even Adruino) may be needed, or at least lower level code that C#.

Windows is not RT OS, so you can't really get sub-millisecond accuracy.

Busy loop (possibly on high-pri thread) as you have is common approach if you need sub-millisecond accuracy.

You can try using Multimedia timers (sample - 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).

In any case you should be aware that your code can loose its time-slice if there are other higher priority threads to be scheduled and all your efforts would be lost.

Note: you obviously should consider if more sensible data structure is more suitable (i.e. heap or priority queue may work better to find next item).

Community
  • 1
  • 1
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • Re-arranging the data structure is certainly on my 'to-do' list, but I wanted to tackle the current problem first, since many more CPU cycles are wasted just spinning than calculating if a message needs to be sent. Anyway, I'll look into a priority queue. – user2008803 Feb 01 '16 at 05:15
1

As you have discovered, the most accurate way to "wait" on a CPU is to poll the RTC. However that is computationally intensive. If you are needing to get to the clock accuracy in timing, there is no other way.

However, in your original post, you said that the timing was in the order of 15ms.

On my 3.3GHz Quad Core i5 at home, 15ms x 3.3GHz = 50 Million Clock cycles (or 200 million if you count all the cores).

That is an eternity.

Loose sleep timing is most likely more than accurate enough for your purposes.

To be frank if you needed Hard RT, C# on the .net VM running on the .net GC on the Windows Kernel is the wrong choice.

Aron
  • 15,464
  • 3
  • 31
  • 64
  • So you'd be suggesting a simple Thread.Sleep(0) for every loop of my thread, or at least, when I loop through the entire list of messages and nothing needs sending? EDIT: Also, the timing between loops of the thread is more like 0.25 milliseconds. I meant to say the thread will never be idle for 15 milliseconds. Heck, 5 is unlikely. – user2008803 Feb 01 '16 at 04:50
  • 1
    @user2008803 After reading about Quanta, yes. But this is no guarantee of it working correctly. My real advice would be to of load the work onto something which is HARD RT, like an Arduino and code the real CAN interface in a RT language like C++. – Aron Feb 01 '16 at 04:55
  • I think I'll have to call that "close enough". Loss of accuracy isn't really a deal-breaker here. This code isn't meant to really be a true CAN interface, this is more like a fake-out box for testing the client code by sending the same messages the server would have sent in a true working environment. – user2008803 Feb 01 '16 at 05:12