Multithreading - Reading Sensors in Sub Threads - How To Optimize Performance

I’ve got a device that I think multi-threading is the way to go, but I want to make sure I’m implementing it in the correct/best way to make sure performance is not affected negatively.

My device is using an interrupt driven state-pattern/machine to operate it normally. Typically, each state runs a tight-ish loop to read some sensors and display them on an GLCD. This runs on off the “main” thread.

There are some sensors that need to ALWAYS read and collect data incremental data which can be displayed from any state. Also, this data needs to be written to EEPROM periodically.

for example(pseduo code)

public static void Main()
{
//setup code...

Thread S2 = new Thread(new ThreadStart(ReadSensor2));
Thread SaveS2 = new Thread(new ThreadStart(SaveSensor2));

S2.Start();
SaveS2.Start();
While(true)
{
     ModeContext.RunStateTask();
}
}


//Run State Task
void MainTask()//this method is within a state class
{
   Sensor1.Update();
   Display.Write(Sensor1.Value);
   Display.Write(Sensor2.Value);
   Thread.Sleep(250);

//in another thread
public void ReadSensor2()
{
     while(true)
{
   Sensor2.Update();
   thread.sleep(250);
}
}

//in another thread
public void SaveSensor2()
{
while(true)
{
   EEPROM.WriteSensorData(Sensor2);
   thread.sleep(30000);//30 seconds
}
}

}

All of the threads would typically run indefinitely.

Can you specify a thread to run while another is sleeping so the main task thread is not interrupted? Am I overthinking this? What’s the best way to implement this? Any tips would be appreciated.

The first approuch that pop’s up to me is using locks.

[url]http://www.dotnetperls.com/lock[/url]

Not easily, especially if you want to guarantee a steady cadence of execution without much ‘jitter’ in the timing. But the statement you just made above is the very definition of ‘sequential execution’ - that is, a single thread with two blocks of code that run exclusive of each other.

If task 1 needs to run every N milliseconds and it takes M milliseconds to complete, then you can use the idle time of N-M to perform task 2. If task 2 takes less than N-M mS to complete, then you’re good. If it takes more than N-M mS to complete then it won’t work sequentially, and no amount of threading magic will make it work either because you only have one CPU core and threading gives you separation of state - not more CPU capacity.

Some follow-on notes: If you decorate any two (or more) methods with this attribute:



then the system will ensure that no two methods with that attribute will ever be run at the same time on separate threads. The system will however delay one of them to await the completion of the other, and it sounds like that’s what you want to avoid, so again, I think a single-threaded loop is you best bet if you want a very stable timing of your sensor reads and other tasks.

I have used separate threads for the sort of thing you are describing (as it does improve modularity and clarity of the code), but only when there is enough CPU speed available such that resource contention (locks being hit) is brief and the impact of those collisions is within my tolerance for timing jitter.

@ David@ Leclanche I was thinking locks too, but wanted to make sure that’s a “good” way to do it.

I’m not too concerned about a steady cadence. I want my main thread which is displaying the data to be smooth and uninterrupted. I don’t want to be half way through updating my display and have some kind of pause giving a clunky user experience. Maybe I lock my display write/SPI transfer method?)

I just want to be always collecting and saving data from some specific sensors so any method can have the current value and I’m so not duplicating too much code in every state or having to worry about missing some readings while in a different state that doesn’t display that sensor data.

Locks don’t guaranteed ‘uninterrupted’, they only offer concurrency guarantees. Your main thread can still be interrupted, though with a small number of threads and by avoiding churning memory (resulting in GCs), it won’t be perceptible.

I have a somewhat similar application I’ve been working on and have come up with an approach that seems to work for me without threads. A lot depends on how fast your data is coming in and how fast you have to do something with it. Care to share your data input and update rate requirements?

I don’t have to display data but I do have to calculate a bunch of stuff based on data from several sensors and write data out to actuators. I use a state machine based on the “Gang of Four” state pattern and an asynchronous FIFO queue that I got from taylorza (https://www.ghielectronics.com/community/forum/topic?id=8083&page=2#msg79627). I modified taylorza’s queue so I can enqueue and dequeue a structure with the sensor data, the current state, an error message, a time stamp and some other stuff. Almost all my sensors are on serial ports so I have a pretty simple event handler (using the standard .NET MF serial class) that does nothing but gather a sensor’s data and stick it on the queue along with the meta-data. My I2C sensors are pretty quick so I just do a regular I2C read/write transaction and stick the results on the queue. Every loop through my GOF state machine, I check the queue and if anything new is there, I process it as needed. I do lock the queue when enqueueing and dequeueing so the interrupt driven serial events can’t interrupt an ongoing queueing/dequeueing process. Not sure if I really have to but I do. I’ve tested this architecture at rates up to 10 Hz on a G400 Raptor and it seems to work fine. It might go faster but I don’t need to go anywhere near that fast so I’m good.