Do you use multithreading in embedded system programming

Status
Not open for further replies.

jab99407

Member
Hi,
I have written all my code so far in the main function inside the while loop. I have been reading various documents but I don't get the exact idea for multithreading. Do you use multithreading in embedded system programming? What is the concept of multithreading in embedded system programming for 32 microcontrollers?
 
Setting everything up then running a loop to do all the ongoing operations is a very common scheme.

Purely software-wise, Multitasking and multithreading are pretty much the same thing.

Generically, multitasking is running different programs simultaneously, and multithreading is different tasks within the same program simultaneously.
(Hardware multithreading is a different thing again).

When writing the entire program for an MCU, whichever you call it is up to you; it's all one program so strictly multitasking is multithreading, in that context.
[And note that multitasking means simultaneous execution as far as the user / peripherals are concerned, it does not imply multiple CPUs; that's different again].

If you write your program so it always skips ahead if any task is not ready or not needing attention, rather than using wait loops that stop execution waiting on things, you are already multitasking / multithreading!

Likewise if you use interrupts and perform actions in those.

If the program never stops & continuously executed the overall loop, with different sections run or skipped as appropriate, to me that fits the original definitions of multitasking/multithreading.
 
If you write your program so it always skips ahead if any task is not ready or not needing attention, rather than using wait loops that stop execution waiting on things, you are already multitasking / multithreading!

Check for three tasks every 1ms ( Waiting, ready to run, running )

T1 is running for 2ms every 10ms
T2 is running for 4ms every 20ms
T2 is running for 6 ms every 30ms

Does it make sense for multithreading?
 
To answer your question as asked, yes. In fact, it is the very rare application that it does not require some kind of concurrency.

I liked it illustrate the problem for newbies with the Arduino LED blinking tutorial. If you want to blink one LED, then it's a simple matter of turning it on, delaying, turning it off, delaying again, and repeating. But if you want a blink two LEDs, asynchronously, you have to resort to a completely different design.

I prefer to use the super loop pattern - as you described when it is a sufficient solution but sometimes, you just can't avoid the fact that certain steps can take so long that other, faster, events don't get checked often enough. I will use as an example the case where one task rebuilds an entire screen full of data and needs a hundred milliseconds to do it or as another task monitors an input and has to react in 10 milliseconds. Of course, you could build the input monitor into the screen remainder but that way lies madness.

There are essentially two kinds of multithreading. First is cooperative and the second is preemptive.

In cooperative multi-threading, each task is like an independent program, usually with its own stack, but it will periodically yield to a supervisor to give other tasks a chance to run. In my example, the screen painter might yield the CPU after every few scan lines, or widgets, or other convenient subdivision completes.

In preemptive multitasking, there is a timer driven interrupt that invokes the scheduler. Any fact, instead of a long task voluntarily giving up the CPU, the supervisor says,"okay, it's someone else's turn now" and forcefully takes away the CPU.

Preemptive multitasking is how general purpose operating systems like Windows, MacOS, and Unices do it. If you want to blink too LEDs and you have a preemptive OS,. Then it's just as simple as the one LED case. You can have two parallel tasks, each of which uses a blocking delay, and the OS will ensure that they both get to execute.

The problem with preemptive scheduling is that there is a danger that the preempted task is in the middle of something critical when it gets interrupted. And the extreme, this can lead to hard to debug interactions with other tasks including deadlocks and race conditions. Therefore, when I can't use a super loop, I try really hard to make cooperative multi-threading solve the problem and only resort to a preemptive scheduler when the other alternatives just aren't good enough.

Within preemptive schedulers there are all kinds of schemes to decide which tasks get to execute when. A common pattern in real time systems is that the highest priority task that is not blocked (waiting for some dependency to be satisfied) gets to run. Say one task with an intermediate priority is running because all higher priority tasks are blocked. As soon as a higher priority task is unblocked, the schedule where preamps and gives the CPU to that higher priority task. There are all books, in fact many whole books, on the subject of scheduling.

The heart of either a cooperative or preemptive scheduler is the context switch. For a simple cooperative scheduler, the contact switch can be handled entirely in C. Preemptive systems almost always require at least a little bit of assembly code for the context switch. It's useful to have a gentle purpose context switch in your library for the processors that you tend to use. Writing a context switch is an excellent learning exercise. But be aware that getting it wrong will result in maddening and subtle bugs in your entire system. Not something you want to do casually.

There are good ready-made small operating systems suitable for embedded systems. FreeRTOS is a relatively lightweight open source OS that can be used either cooperatively or preemptively. There are also a number of commercial products of which VRTX is a widely used example. I tend to write my own, because I can optimize for a given application and I like the challenge. But I have used FreeRTOS once and it was trouble free if a little bigger than what I really needed. I have not used a commercial embedded RTOS.
 
Last edited:
Most OSs that support preemptive MT, have an equivalent of Critical Section objects; which can (mostly) completely eleviate the "in the middle of something critical" problem.

Of course, they can be abused; but so can yeild().

(IMO) it is much easier to bracket truely critical sections than it is to guarentee that every piece of code will always yield sufficiently frequently to ensure that the most time critical code segment always gets a timely look in.

And when things change, and they always do. Ie. the most time critical code segment needs to run every 8.33ms rather than every 10ms; Or the longest uninteruptable code segment needs an extra 2ms; the cooperative route will require changes thoughout the code base, where the preemptive code will only require changes to the code sections who requirements have changed.
 
Fair enough, Buk. To a certain extent, it depends on the kinds of systems you work with. But I will admit that I probably err on the side of using super-loops and cooperative MT when I should be trusting a pre-emptive OS to do it's job and make my life easier. Both FreeRTOS and VRTX support critical sections and semaphores. I can't actually think of an RTOS that doesn't at least have critical sections, although some simple kernels may do it by disabling all interrupts.
 
Reactions: Buk
In an embedded system, YOU decide what will happen and when. In a multitasking system ANY software could be running at any time. I have not come across a problem where multitasking is needed for any embedded system.

Mike.
 
I have not come across a problem where multitasking is needed for any embedded system.
It's not a case "need", but rather one of 'makes life easy'.

Too many times I've seen hundreds, or thousands of man hours go into tuning an event loop implementation; only to see a final additional requirement completely ruin all that effort.

Preemptive multi-tasking allows you to write each sub function of the application -- monitoring the keypad; updating the screen; responding to an interupt; polling a sensor; calculating whatever -- as if it was the *only* task that needs to be processed. Isolated and standalone. And then prioritise those tasks so that a preemptive OS ensures that each task gets timeslices commensurate with its needs.

Think about the combination of software that you are running right now as you read this. In my case, I have two browsers talking to a combined total of 43 websites. Several of which are constantly re-checking the website for updates -- eg. email. One is paused playing a episode of Doctor Who; another is paused playing a video on the aerodynamics of contra-rotating ducted fans.

I also have 3 instances of my CAD program running; a PDF viwer; and image viewer; a 3D printer slicer; plus all the standard OS stuff -- ethernet, wifi, USB, bluetooth, etc. etc, etc.

Imagine if the development teams of all those pieces of software had to collaborate in order that the programs could coexist!

Preemptive MT means they do not have to.

If each of your embedded systems does something very simple that never changes, you do not need MT. Otherwise, it just makes life simpler.
 
That is not what I would consider embedded. Do you have an embedded example?

Mike.
 
Think about the combination of software that you are running right now as you read this.
.. On a operating systems designed for end-users to be able to add and remove software from different places. No comparison to an MCU program created by a single person.


I agree totally with Mike, in the sense that "RTOS" or whatever is redundant, in an MCU system where "you" are writing the entirety of the program - anything such an add-on can do, you can do better and faster yourself in your own program.

The last thing you should be doing if things are time-critical is adding extra code and overhead!

It is a totally different situation if an operating system is required for other reasons - or where the end user can add and remove software - but for a single-program MCU, a good programmer can manage things themselves.

There are a lot! of programmers who just cannot grasp the fundamentals of multitasking within a single program, who use such as RTOS to allow badly written programs to function.
It does not mean it's a good way of doing things.
 
Do you have an embedded example?
Does a smartphone count as an embedded application?

A real application from circa 25 years ago.

M&S introduced hand held stock control units into their stores. The units used radio (not cellular or blue tooth) connections to a server attached reciever in the stockroom. They had an 8 line LCD display, a numeric keypad, a 5 positon cursor pad, a barcode reader, RS422, and a radio module that acted like an RS232 connection. The terminals were built by an american company whos name I do not remember, used NEC V80 processor and a RTOS called RX-UX.

M&S were big on specifying and managing the development of all the software their systems used, and so an event driven Control Program was designed and specified by the M&S mainframe software team using the Waterfall method, and implemented off-site by the terminal supplier's software guys with monthly progress review meetings. It took 10 or 12 guys, 18 months of iterations to develop and (eventually) functioned perfectly in the test lab at M&S.

As soon as it went into the field it was a disaster. The terminals would freeze up and have to be manually reset by an engineer. In the real world with lift motors in adjacent shops, freezer and chiller motors cutting in and out at random, flourecent lighting buzz etc. etc. Instead of getting 9600baud bidirectional comms, they were lucky to get 300 baud one way.

I was brought in to rewrite the server end. The original server program was another behemoth event loop running under DOS that was designed to talk to up to 16 terminals at a time, either via hardwired RS422 or RS232 over radio. It also had to download data from the terminals and upload it to the companies network; and download pick lists, price lists and stuff from the network and upload them to the terminals.

I rewrote the server sofware as a suite of 10 separate programs that ran under OS/2. Several of the programs would have one instance running for each terminal that was being talked to.

Each was a very simple, linear flow: make connection, transfer data; disconnect from terminal; set a work item (upload or download) for the network comms program to pick up when it was ready; end.

Another program would run constantly round robin over the comms ports, and if one of the terminals was attempting to connect, it started a new instance of the appropriate program to handle that; and went back to round robin.

The network comms program ran constantly, periodically polling a directory on the server for the presence of work item; and periodically checking a directory on the network for work items generated at the mainframe end. When it detected either, it started an instance of a single file transfer program to take care of the work item. and went back to watching.

The beauty of the system was that each of the programs was entirely stand alone and could be modified entirely independantly of each other. It took me 2 months to design and implement the entire suite.

Another independant specalist was brought into re-write the terminal software. Whilst I didn't have any part in writing that software, I had to work closely with him for an extended period. He did almost exactly the same thing. Instead of a single, monolithic, time critical loop that attempted to juggle all the sub routines in a timely manner using cooperative methods as the original did, he wrote a bunch of inependant tasks and used the RX-UX scheduler to manage them. I can't go into any details because I don't know them. I do know that he had his stuff working before I was ready. 1 man and 2 months.
 
Last edited:
Does a smartphone count as an embedded application?
Definitely not.

How is a smartphone embedded. It runs a full operating system and runs any software at any time, exactly what I said wasn't an embedded system.

Mike.
 
No comparison to an MCU program created by a single person.
It wasn't meant as a direct comparison. Just an example of the virtue of separating the scheduling of tasks, from the code that performs those tasks.

I did finish with:
If each of your embedded systems does something very simple that never changes, you do not need MT.
 
How is a smartphone embedded. It runs a full operating system and runs any software at any time, exactly what I said wasn't an embedded system.
I guessed not, hence going on to describe the hand held terminals.

It just struck me that even the cheapest, lowest power smartphone could perform every single function of those hand held terminals without skipping a beat (or a twitter notification).
 
I did finish with: If each of your embedded systems does something very simple that never changes, you do not need MT.

Multithreading/multitasking IS needed and used in any serious embedded MCU application - but via different approaches; it just does not need any additional "operating system" to do it.

It's typically implemented by a mix of cooperative (in the main tasks, executing as needed) and pre-emptive (interrupt controlled).
 
That's what I am trying to understand, for which I have also given an example
 
That's what I am trying to understand, for which I have also given an example

T1 is running for 2ms every 10ms
T2 is running for 4ms every 20ms
T2 is running for 6 ms every 30ms

Typical MCU tasks rarely have fixed execution times, and rarely need fixed time allocations, other than very short ones, so that seems a very artificial example.

If I had to enforce that schedule, I'd have a regular interrupt "clock" at 1KHz (1mS) or a convenient multiple of that frequency, eg. 10KHz or whatever works in the overall program.

Also divide down further and keep track of the 1mS, 10mS, 100mS increments etc.

Every 10mS:
On the 0mS (of the 10 count) it sets a flag bit to say T1 is due.

On every 2mS (of the 10) it checks the 10s and if that is even, it sets a flag bit for T2

On every 6mS (of the ten) it updates a 0-2 counter and sets a flag bit when that overflows, to say T3 is due.

The main program loop calls T1, T2 or T3 if their respective flag bit is set, then clear the bit.

Remember that a basic single core CPU as in typical MCUs cannot execute more than one instruction at a time - at the machine code level, only one program section is being executed at any one instant.

And note that desktop operating systems have been multitasking and running multithreaded programs since well before multiple CPUs / multi core CPUs or speculative execution became common. A basic single core CPU can do those things perfectly well.

If you control the execution and priority sequence, you know what will happen when. If you leave it to some overriding third party system, you don't - and that other system is taking clock cycles from YOUR program.

Edit - Typo corrected.
 
Last edited:
If I had to enforce that schedule, I'd have a regular interrupt "clock" at 1KHz (10mS) or a convenient multiple of that frequency, eg. 10KHz or whatever works in the overall program.

Except 1KHz is 1mS

However, the premise is correct, simply use interrupts to set flags that are detected and operated on in the main program loop. And 1mS is a commonly use timer setting for such purposes - and can also run an RTC.
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…