TeensyThreads - even an empty while loop in a thread delays the main loop

jevgienij

New member
Code:
#include <TeensyThreads.h>

long time;

void setup()
{
	Serial.begin(115200);
	threads.addThread(loopSec);
}

void loop()
{
	time = micros();
	analogRead(1);
	Serial.println(micros() - time);
}

void loopSec()
{
	while(1)
	{
		
	}
}

Teensy 4.1. In this example analogRead takes 17μs. If I add an empty thread it will still be the same. But as soon as I add the while(1) loop, some readings will suddenly take over 11000μs. It's hard to catch it with analogRead going so fast but if I write to an LCD which takes about 3ms to complete, I get 1 in a 5 writes take 14ms instead of 3ms.

Is this how the threads are supposed to work or am I missing something?
 

Attachments

  • javaw_F8JMc8rmgL.png
    javaw_F8JMc8rmgL.png
    30.6 KB · Views: 32
  • javaw_jjuiuvQTfz.png
    javaw_jjuiuvQTfz.png
    30.8 KB · Views: 28
With the loop that's not what I call 'empty', its busy-looping, stealing the CPU until pre-emption occurs.
 
equal priorities: round-robin scheduling

First answer: The program as written has asked the the executive to run two threads with equal priority. Both threads ("tasks") are written to run continuously, so the apparent gap in execution of the loop() thread is the execution of the loopSec() thread. The Teensy 4.1 has one processor core, so the threads share CPU time in a round-robin sheduling cycle. I am guessing that the default timeslice of a thread defaults to 10 msec or so. So each thread gets about 10 msec.

Traditionally, each thread (or back in the day, "task") has a priority, a timeslice (maximum continuous runtime unless interrupted by hitgher priority task), and a way to specify how to trigger its execution. When tasks have equal priority and no other way to specify how they get scheduled, they execute one after the other under the time limit specified by their respective timeslices.

Just for fun, try an experiment. Use the teensythreads call setTimeSlice(int id, unsigned int ticks) to limit the execution time of the forever loop. I assume (having never used teensythreads) that the executive will give loopSec 3 millisecond timeslices and will give the main loop 10 millisecond timeslices. If it works the way I would assume, your main loop will see time gaps of around 3 msec instead of 13 msec.

Code:
void loopSec()
{
        setTimeSlice(id(), 3);
	while(1)
	{
		
	}
}

Most developers who use preemptive multitasking systems give tasks that they want to run predictably higher priority, and with either a time-based or event-based trigger.

Real time operating systems ("executives") and the many many approaches to specifying their behavior has been a research topic for decades, this comment does not begin to cover the topic. :)

I used ftrias' teensythreads github reference for this comment.
 
You're right, because you have two threads, LoopSec() and loop(). If loop() is preempted between calls to micros(), the reported time will reflect the time slice given to LoopSec(). If you want to make sure that never happens, you can either lock/unlock the scheduler around the call to analog read, or make the time slices very long and use yield() to switch threads cooperatively.
 
Back
Top