Timers vs Threads

@ NicolasG - You can get sub 20 ms execution if things are done right. You can have for example a timer set to 1 ms and it will execute at that rate ( ignoring garbage collection for now) but if you have another thread that does not give up time willingly then your right it will be in that 20 ms range. For me I try to never use hard loops for anything if I can find another way to do it. Lots of ways to do things.

@ Cowboy - Your’re right. That should work with something like this in the Main() thread.

           
 while (true) Thread.Sleep(1000);

I will try this later.

I thought the 20ms time slice was like a worst case scenario, so as long as the thread isn’t waiting for a shared resource it’ll execute without any delay?

@ wolfbuddy - Only if there are no other threads currently executing.

A thread is executed until the end of its time slice OR the end of the thread function OR a Thread.Sleep() call (maybe others?).
Then the scheduler switch to next thread. It can be the same thread if there is only one.

@ Cowboy AND @ NicolasG - Thanks for the info.

I’ve just been testing out the threaded version of my program and I’m really pleased so far., mainly because the program still actually works! :smiley:

The input polling thread is polling 3 inputs every loop, 3 more every 4th loop and 3 more again every 10th loop. This means I’m getting the most critical inputs polled at 25-30ms intervals, the next critical every 110-130ms and the least critical every 315-335ms. I really like this because it means that I can easily control how often a set of inputs are polled and most of the time the polling thread executes at less than 45ms intervals. Every 20th iteration of the loop all 9 inputs are polled and this takes ~60ms.

To achieve this with timers I don’t know of any other way than to try and synchronise 4 different timers at different intervals and offsets, which seems more complex especially if I wanted to add/remove inputs or change their logging frequency.

I already know that the inputs polled at the 315-335ms interval is more often than I intended, which was ~500ms, so I can easily just alter a variable to poll them every 15th loop.

Glad its working for you. What kind of inputs are you polling?

They’re actually CAN messages, and I have to send one first to request what I want to log. It can take up to 10ms to get a response so when I was trying to get 9 messages back in one timer I wasn’t able to get any of them back faster than 120ms. This way the ones I want to log at 25hz aren’t held up by the ones I don’t need to log at anything higher than 2 or 3 hz.

Wich hardware do you use for CAN?

Sparkfun CANbus shield.

I made the class below to decouple a process that reads X10 data from multiple sources from a consumer that uploads it to a server. It is a circular buffer as RoSchmi suggested.

It doesn’t solve your polling problem but might help with decoupling the SD card writing.

In my use case I write to the buffer in callbacks and spawn background thread to read the data. The background thread waits when it has no data and resumes as new data arrives. This is how I use it to read data (decodeBytes does the processing):

...
            Thread t = new Thread(new ThreadStart(ConsumeBytes));
            t.Start();
...
        private void ConsumeBytes()
        {
            while (true)
            {
                // read 5 bytes starting with hex D5
                var buffer = _autoBuffer.ReadBytesFrom(0xD5, 5);
                decodeBytes(buffer);

            }
        }

The full class is below. Hope it is useful and welcome any comments.:


using System;
using Microsoft.SPOT;
using System.Threading;

namespace x
{
    /// <summary>
    /// circular buffer used to decouple a consumer from a producer
    /// note this is not suitable for multiple consumers
    /// note the data will be overwritten if the producer fills the buffer
    /// </summary>
    public class AutoByteBuffer
    {
        /// <summary>
        /// Change this to set the size of the buffer
        /// </summary>
        private static int BufferSize = 1024;
        private Byte[] _buffer = new Byte[BufferSize];
        private int _wrterPos = 0;
        private int _readerPos = 0;
        private static AutoResetEvent autoEvent = new AutoResetEvent(false);
        private object _lock;

        public AutoByteBuffer()
        {
            _lock = new object();
        }

        /// <summary>
        /// Write an array of bytes to the buffer. Releases any threads waiting for data.
        /// </summary>
        /// <param name="data">data to write</param>
        public void Write(Byte[] data)
        {
            lock (_lock)
            {
                foreach (var dataByte in data)
                {
                    _buffer[_wrterPos] = dataByte;
                    _wrterPos++;
                    if (_wrterPos >= BufferSize) _wrterPos -= BufferSize;
                }
                autoEvent.Set();
            }
        }


        /// <summary>
        /// read one byte from the buffer. if there is no data suspend until new data arrives
        /// </summary>
        /// <returns>one byte</returns>
        public Byte ReadByte()
        {
            while (_wrterPos == _readerPos)
            {
                autoEvent.WaitOne();
            }

            lock (_lock)
            {
                var retVal = _buffer[_readerPos];
                _readerPos++;
                if (_readerPos >= BufferSize) _readerPos -= BufferSize;

                if (_readerPos == _wrterPos)
                {
                    autoEvent.Reset();
                }
                return retVal;
            }
        }

        /// <summary>
        /// read a sequences of bytes from the buffer and wait if there are not enough
        /// </summary>
        /// <param name="count">number of bytes to read</param>
        /// <returns>an array of length count of bytes</returns>
        public Byte[] ReadBytes(int count)
        {
            var retVal = new Byte[count];
            for (int i = 0; i < count; i++)
            {
                retVal[i] = ReadByte();
            }
            return retVal;
        }

        /// <summary>
        /// read a series of bytes starting with thr first byte that matches a particular value
        /// </summary>
        /// <param name="startVal">the value to look for</param>
        /// <param name="count">the number of bytes to read</param>
        /// <returns>an array of length count of bytes</returns>
        public Byte[] ReadBytesFrom(Byte startVal, int count)
        {
            var retVal = new Byte[count];
            while (ReadByte() != startVal) { }

            retVal[0] = startVal;

            for (int i = 1; i < count; i++)
            {
                retVal[i] = ReadByte();
            }
            return retVal;
        }

        
    }
}

Thanks for the code Nick.

Thank you for your interesting code Nick,
you asked for comments. Since I am just writing a circular buffer there are some things, I can say. First of all I think that the code is well structured in contrast to that what I have coded until now. But there are some minor things I have to mention:

  1. As far as I see your Write method has no protection that wrterPos overtakes readerPos. Propably you are sure that your circular buffer never gets full.
  2. In the methods ReadByte and ReadBytesFrom in var retVal = new Byte[count] you create a new byte array in each call of the method. This produces work for the GC. Solutions to avoid this would be more complex.
    Regards Roland

Hi Roland

On the buffer overflow problem I thought about using the same mechanism (AutoResetEvent) to block the write if it fills the buffer but decided to let it overflow rather than block the producer and add it as a note in the XML docs. If the reader can’t keep up I would have to re-design the system anyway!

I like the idea of not creating a new byte array each call. Perhaps the ReadBytes method could be passed a byte array to fill and return once it was full. The consumer would then have a single static buffer that was re-used. Great idea!

Thanks

More extensive testing has showed that my program version with threads is not running as smoothly as I first thought. After about three and a half minutes of logging both threads slow down noticeably, for example my polling thread (highest priority) goes from 40ms per loop to 200ms per loop, and the second thread (normal priority) that refreshes the displayed information slows from 400ms to nearly 2 seconds!

So far the only ideas I’ve been able to come up with are that I have a memory leak, or something odd is going on with the garbage collector. I’m still trying to understand fully how the GC works, and going through my code to see where I’m repetetively declaring variables (ironically like Roland has commented on Nick’s code).

Does this sound like I’m on the right track? Is there anything else that could be causing this sudden slowing down of my program?

Many thanks in advance.

Well it doesn’t look like there’s a memory leak, I just checked and free memory is hovering around 50kb.

Could it be that the scheduler is doing this for some reason? Attached is a chart that shows what’s happening.

@ wolfbuddy -
Hi, hard to say anything without seeing your code and knowing what the values in your chart exactly mean.

It’s time between each iteration of the thread, recorded at the end of the method before the data is written to the SD card. The trend is the same for another thread that refreshes the displayed information, which I checked by debugging the time that the display refresh thread executes. It’s always after about 4300 iterations.

Ooh, I just thought. I should record the time after the write to the SD card is complete as well so that I can see how long that process is taking.

On the desktop an AutoaResetEvent wraps a kernel synchronization object known as an Event (this object supports both manual and auto reset functionality). The problem with kernel objects is that every access to them requires a transition from user mode ring 3 code to kernel mode ring 0 code. I could go into the details of this transition, but suffice to say that the cost of the overhead is significant.

In contrast user mode synchronization objects do not require this transition and therefore perform much better.

The advantage of the kernel objects is that they can work across processes while the used mode synchronization objects are limited to thread synchronization within a the owning process.

Of course that is all for the desktop, on .NETMF platform I would guess that the overhead is significantly less since the platform does not support code running at different security levels. Of course this should be tested, but I would not assume the same performance characteristics on .NETMF.

@ taylorza - Hi taylorza, Interesting to hear that things can behave different in NETMF. Always interesting to find out if there is a bottleneck, where the bottleneck is and how code can be optimized.
Regards Roland