views:

372

answers:

9

Hello,

my C# application reads data from special USB device. The data are read as so-called "messages", each of them having 24 bytes. The amount of messages that must be read per second may differ (maximal frequency is quite high, about 700 messages per second), but the application must read them all.

The only way to read the messages is by calling function "ReadMessage", that returns one message read from the device. The function is from external DLL and I cannot modify it.

My solution: I've got a seperate thread, that is running all the time during the program run and it's only job is to read the messages in cycle. The received messages are then processed in main application thread.

The function executed in the "reading thread" is the following:

private void ReadingThreadFunction() {
    int cycleCount;
    try {
        while (this.keepReceivingMessages) {
            cycleCount++;
            TRxMsg receivedMessage;
            ReadMessage(devHandle, out receivedMessage);

            //...do something with the message...
        }
    }
    catch {
        //... catch exception if reading failed...
    }
}

This solution works fine and all messages are correctly received. However, the application consumes too much resources, the CPU of my computer runs at more than 80%. Therefore I'd like to reduce it.

Thanks to the "cycleCount" variable I know that the "cycling speed" of the thread is about 40 000 cycles per second. This is unnecessarily too much, since I need to receive maximum 700 messagges/sec. (and the device has buffer for about 100 messages, so the cycle speed can be even a little lower)

I tried to reduce the cycle speed by suspending the thread for 1 ms by Thread.Sleep(1); command. Of course, this didn't work and the cycle speed became about 70 cycles/second which was not enough to read all messages. I know that this attempt was silly, that putting the thread to sleep and then waking him up takes much longer than 1 ms.

However, I don't know what else to do: Is there some other way how to slow the thread execution down (to reduce CPU consumption) other than Thread.Sleep? Or am I completely wrong and should I use something different for this task instead of Thread, maybe Threading.Timer or ThreadPool?

Thanks a lot in advance for all suggestions. This is my first question here and I'm a beginner at using threads, so please excuse me if it's not clear enough.

A: 

I'd suggest getting a profiler, or use the one in VS2010. You need to identify the bottleneck, rather than randomly changing a few things here and there to see if it will suddenly work.

Carlos
I bet the profiler will say 90% of time is spent in the ReadMessage method. If you call it 40,000 times for every 700 messages this is to be expected, but knowing this would help how?
Cobusve
Profiling tells you whether what you think is going on IS actually going on. You might be doing something unexpected, or something unexpected could be slowing down the system.
Carlos
A: 

What does ReadMessage return if there are no messages left ?

You should only sleep the thread if it tells you that there are no messages. Sleeping for 1ms in this case only should do the trick.

Cobusve
-1: Depending on how the system is being utilized at the time, Thread.Sleep(1) may sleep (much) longer than 1ms. Therefore this could result in dropped messages.
Daniel Rose
A: 

I would do:

//...do something with the message... 

// If the message is null, it indicates that we've read everything there is to be read.
// So lets sleep for 1ms to give the buffer chance to receive a few extra messages.
If (theMessage == null) // Buffer is empty as we failed to read a new message
{
    Thread.Sleep(1);
}

This way probably gives you maximum performance, other than hammering your CPU.

SLC
-1: Depending on how the system is being utilized at the time, Thread.Sleep(1) may sleep (much) longer than 1ms. Therefore this could result in dropped messages.
Daniel Rose
Wrong. The buffer is only 100 messages in size, therefore if we assume its 1000 messages / second thats one message per millisecond (it's actually 700, so even more relaxed). This means it takes 100ms for the buffer to fill up. Sleeping for 1ms, even if its 50ms, is not a problem. Remember, it only pauses when the buffer is empty, not each cycle. In addition, ANY other solution is going to have to suffer with the same inaccuracies. Whatever method you choose to slow it down, you'll never be able to get it exact.
SLC
@SLC you cannot know how long exactly it will pause. It might be so long until the sleep returns that messages are dropped. The solution using a separate multimedia or system timer seems to me to be the best solution.
Daniel Rose
@Daniel How do you stop your timer/stopwatch from using 100% cpu?
SLC
@SLC The timers don't use 100% CPU while they have not yet fired.
Daniel Rose
@Daniel So during that time, the operating system has control, not your application... exactly as if you'd done Thread.Sleep(1)...
SLC
@SLC Thread.Sleep() means: I don't want my timeshare anymore. Please reschedule me to some other time. Timers means: Please notify me (in general ASAP) after this time elapsed. So I'd imagine that you can get higher guarantees from timers.
Daniel Rose
I already explained how the reliability issue is countered. Can you demonstrate who Monitor.Wait is any more accurate? There is no documentation to support your theory.
SLC
A: 

Thread.Sleep also means giving up your thread's timeslice. You will return to the thread only after it is scheduled again. That is why you are "sleeping" longer than 1ms. See also this post by Peter Ritchie.

The problem seems to be that you have to actively poll the DLL to get new messages. A better solution would be having the DLL notify you when a new message has arrived. However, you said that you cannot modify the external DLL.

What you could try would be reducing the priority of the polling thread.

EDIT:

You could for example use a DispatcherTimer to fire approximately every millisecond. The DispatcherTimer won't thrash your CPU while the event has not yet fired. In the handler you could then process messages (I assume you can tell if there currently is no message) until there is no message left to process. You would also have to safeguard against reentrancy, i.e. getting into the event handler while it is already running.

As code example:

// Creating the DispatcherTimer
var dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = TimeSpan.FromMilliseconds(1);
dispatcherTimer.Start();


private void dispatcherTimer_Tick(object sender, EventArgs e)
{
    if (this.ProcessingEvents)
        return;

    this.ProcessingEvents = true;

    var messagesLeft = true;

    while (messagesLeft)
    {
        TRxMsg receivedMessage;
        ReadMessage(devHandle, out receivedMessage);

        // Process message
        // Set messagesLeft to false if no messages left
    }

    this.ProcessingEvents = false;
}
Daniel Rose
A: 

How about using an AutoResetEvent with a timeout of 1 millisecond?

private void ReadingThreadFunction() {

    try {
            var autoReset = new AutoResetEvent(false);
            autoReset.WaitOne(1);
            TRxMsg receivedMessage;
            ReadMessage(devHandle, out receivedMessage);

            //...do something with the message...
        }
    }
    catch {
        //... catch exception if reading failed...
    }
}

EDIT: Definitely check if messages are available like the other answers suggest as well...

Adam Driscoll
+2  A: 

Use a timer that is high precision. You should try to use Multimedia timer. It will help you in keeping CPU usage less. I am not sure of resloution, but for a constant time (where you dont keep changing the time interval, it is fairly accurate). You can keep a 10 msec resolution and for every invocation of timer, you can read the entire buffer till it is empty or say 100 times. This should help.

Sudesh Sawant
System.Timers.Timer has ms resolution, reading this around 100-200 times/sec should do the trick for you
jishi
No, System.Timers.Timer does NOT have millisecond precision. It goes down to roughly 16-17ms per tick. I learned this through trial and error. Multimedia timer is good for higher precision.
Carlos
I'm pretty sure high precision timers will not help you here. They're good for measuring things, but you want to free up the CPU, not measure something. I would suggest implementing my solution below, because I am confident that it's the correct one.
SLC
@SLC - yes, you're right, I tried it and it works just fine... all messages are received and CPU consumption is no longer a problem... Thanks a lot to all for your help!
Ondra C.
@SLC, the solution you have provided above is good and simple. But the only problem with that solution could be that if you are continuously receiving messages, you would hog up the CPU from doing other processing. Having said this I also am aware that with timer, you may enter a scenario where you have dropped of messages with the buffer becoming full. Hence I suggested the approach with MM timers. Anyways, I believe whatever works for a scenario works; and if is well thought about, is the right solution.
Sudesh Sawant
@Sudesh Unless your computer is too slow to process the messages, it will eventually empty the buffer and thread sleep, so it won't hog the CPU.
SLC
A: 

You could try Thread.SpinWait:

SpinWait essentially puts the processor into a very tight loop, with the loop count specified by the iterations parameter. The duration of the wait therefore depends on the speed of the processor.

Contrast this with the Sleep method. A thread that calls Sleep yields the rest of its current slice of processor time, even if the specified interval is zero. Specifying a non-zero interval for Sleep removes the thread from consideration by the thread scheduler until the time interval has elapsed.

SpinWait is design to wait without yielding the current timeslice

It is designed for situations where you know you'll want to do something in a very short time so losing you timeslice will be excessive.

SwDevMan81
-1: SpinWait will fully utilize the processor while it is running. This is precisely what the OP wanted to avoid.
Daniel Rose
A: 

I think what you should do is use a singleton class with a timer. What this class does is gives you 700 messages per second and if it takes a bit more it waits and retries for 700 messages every second. You can instantiate it on another thread. The singleton is thread safe.

// Queue Start
ProcessQueue.Instance.Start();

// Queue Stop
ProcessQueue.Instance.Stop();

Example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using System.Diagnostics;

namespace Service.Queue
{
    /// <summary>
    /// Process Queue Singleton Class
    /// </summary>
    class ProcessQueue : IDisposable
    {

        private static readonly ProcessQueue _instance = new ProcessQueue();
        private bool _IsProcessing = false;
        int _maxMessages = 700;

        public bool IsProcessing
        {
            get { return _IsProcessing; }
            set { _IsProcessing = value; }
        }

        ~ProcessQueue()
        {
            Dispose(false);
        }

        public static ProcessQueue Instance
        {
            get
            {
                return _instance;
            }
        }


        /// <summary>
        /// Timer for Process Queue
        /// </summary>
        Timer timer = new Timer();

        /// <summary>
        /// Starts Process Queue
        /// </summary>
        public void Start()
        {
            timer.Elapsed += new ElapsedEventHandler(OnProcessEvent);
            timer.Interval = 1000;
            timer.Enabled = true;
        }

        /// <summary>
        /// Stops Process Queue
        /// </summary>
        public void Stop() {
            _IsProcessing = false;
            timer.Enabled = false;
        }

        /// <summary>
        /// Process Event Handler
        /// /// </summary>
        private void OnProcessEvent(object source, ElapsedEventArgs e)
        {
            if (!_IsProcessing)
            {
                _IsProcessing = true;

        for (int i = 0; i < _maxMessages; i++)
                {
            TRxMsg receivedMessage;
            ReadMessage(devHandle, out receivedMessage);
        }

                _IsProcessing = false;
            }

        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                timer.Dispose();
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

    }
}
JeremySpouken
A: 
  1. Verify that you fully understand the API your DLL is exposing. Is there any way to use the library to notify you of a new message's arrival? I'd be surprised if a library such as you describe has no signalling or notification built in to it.

  2. If you must poll, then when you check for messages, retrieve all messages. Don't just retrieve one and then go to sleep again. Get all the messages until there are no more to get because you want to buffer to be clear.

Greg D