Common practice - multiple threads

I’m building the driver for my new module (will be soon released :wink: )
I have one method that switches leds on, and one that switches them off.
Because the ‘turn on’ and off process takes a few seconds, I started a new thread to do that work.
My problem is that if I Call TurnOn and TurnOff immediately, I get nonesense as output, because every threads sends it’s commands via SPI to the led controller.

What is the common practice to solve the problem?
I thought about -before starting a new thread - checking if one thread is running, and stopping it?
Or does anyone know another, better solution?

Regards

1 Like

You need to use synchronization primitives. Read about events, critical sections, semaphores etc. In your case I would use lock statement (equivalent of the critical section) to make sure that SPI communication is thread safe.

What kind of module?

2 Likes

I would build an I/O queue. That way you don’t hold up the calling process.

Ah the lock statement! I’ll try that first, and take a look if it works as expected. I’m not sure about what happens if a few more threads are started. Do they continue in the correct oder?

@ Cowboy - can you explain it a bit?

If you want to ensure an order, the lock statement probably is not enough. In this scenario a set of event objects (auto reset or manual reset events) might be more appropriate. If you give more details, we might narrow it down better.

Oh sorry I forgot to talk about the module:
It uses the WS2803 Led driver. You can control 18 outputs.
One method starts fading on one output after the other, so it takes lets say 5 seconds until all are turned on.
I put that loop into a thread.
If the turn off method is called during the ‘fade on’ period, I have the problem that every thread sends it spi commands.
So what would be the best way to do in that situation?
In my opinion it would be the best to stop the ‘fade on’ thread and start the ‘fade off’ thread immediately.

1 Like

@ dominik38 -
Things to think about::

  1. Do I want a command to complete before another can start.
  2. Do I want to override a command that is currently running.
  3. Do I want a command to have priority IE go first.

Sounds like you have just two commands START, STOP.

The Queue approach simply builds a list of commands and starts the driver if its not running. This way commands can be added to the list while a command is being processed IE First in First out. When the driver finishes a command it looks to see if there is anything in the list to do if so it pulls from the list and processes the command. If you want to override a command then the driver can look in list as it is processing to see if any new commands are there and stop the current command and then start the new command.

There are many ways to do things but I find a queue keeps things neat and tidy.

Hi,
I would Queue the commands and use a lock on the file to processes them…

here is something to get you started, note I didn’t test this … but you get the point…


        private static readonly Queue myCmdList = new Queue();
        private static readonly AutoResetEvent myCmdListEvent = new AutoResetEvent(false);
        static readonly Timer myCmdListTimer = new Timer(new TimerCallback(MyCmdTimer), null, -1, -1);


        // <summary>
        // Adds a request object to be processed
        // </summary>
        public static int AddRequest(object request)
        {
            lock (myCmdList)
            {
                myCmdList.Enqueue(request);
            }

            myCmdListEvent.Set();

            return myCmdList.Count;
        }


        public static void TriggerLed(bool state)
        {
            lock (myCmdList)
            {
                if (AddRequest(new TriggerMyLed() { Led1 = state }) == 1) myCmdListTimer.Change(50, 100);
            }
        }

        private static void MyCmdTimer(object arg)
        {
            int count;
            TriggerMyLed myNextTrigger = null;
            lock (myCmdList)
            {
                count = myCmdList.Count;
                if (count <= 0)
                {
                    myCmdListTimer.Change(-1, -1);
                }
                else
                {
                    myNextTrigger = (TriggerMyLed)myCmdList.Dequeue();
                }

                if (myNextTrigger != null)
                {
                    Debug.Print(myNextTrigger.Led1.ToString());
                    //do something with  myNextTrigger.Led1 here with your code...
                }
            }
        }

        private class TriggerMyLed
        {
            private bool _ledState;
            public bool Led1
            {
                get { return _ledState; }
                set
                {
                    _ledState = value;
                }
            }

            internal TriggerMyLed()
            {
                //... you can something here do your thing here
            }
        }

and you would call it from your code like this


TriggerLed(true);
TriggerLed(false);

4 Likes

Wow, thanks for the replies!
I will try it out as soon as possible :smiley:

let us know if that works :slight_smile:

Just implemented your code, but can’t test it this weekend! :-@
Will give an update soon :smiley:

great let us know :slight_smile: if you’ve changed anything…

dear all,

just did a quick test with simple ‘TurnOn’ ‘TurnOff’ methods - that worked fine :smiley:
Next step is to implement the methods that require threading like ‘FadeOn’ and ‘FadeOff’.
I hope I can do it next week. Don’t have enough free time at the moment :frowning:
And the next problem is that during testing, I indicated a fatal error in my layout, so I have to throw the actual boards away and have to design new ones :frowning:
So the release date delays about 4-8 weeks …but I have another module that will be released very soon :wink:
Just working on the driver & presentation :slight_smile: