Concurrent, Parallel, and Interrupt-Driven Programming
Doing More at Once: A complete guide
Many problems cannot be represented using a simple, single-threaded execution pattern. When we need a system to do multiple things, we must turn to more advanced concepts like concurrent and parallel programming.
Parallel Programming
Parallel programming allows a computer to execute multiple threads at the same time. This is possible if a computer has a multi-core processor, which allows it to simultaneously execute a separate program in each of its cores.
The fundamental building blocks of parallel programming are Fork and Join.
Fork: After a fork, two (or) more software threads run in parallel.
Join: These threads can be combined back into one using a join. At the join, execution will wait until all threads above the join are complete.
Analogy: Digging a Hole with Friends
Imagine if I want to dig a hole in my garden. I invite three friends over for help. The task operation changes from me digging alone to all four of us. The four digging tasks are run in parallel. The four of us cooperate simultaneously and work towards a common goal. When the task is done, the join operator causes my friends to go away, and I work alone again.
A complex system may also employ multiple microcontrollers, each running its own software. We classify this configuration as distributed (or) parallel programming.
Concurrent Programming: Juggling Multiple Tasks
Concurrent programming is different. It allows the computer to execute multiple threads, but only one at a time.
One way to implement concurrency on real-time systems is to have a hardware-triggered software action. It is a parameter-less subroutine call triggered by a hardware event.
Interrupt-Driven Systems: The Heart of Concurrency
The second component of interrupt-driven systems is the software, often called the ISR (Interrupt Service Routine).
This creates two “threads” of execution:
The background thread is the execution of the
mainprogram.The foreground thread is the execution of ISRs.
Analogy: Reading a Book
Imagine you are sitting on a chair reading a book. Reading a book is the background thread (low) main program. You start reading one page at a time in a sequential fashion.
You might jump to look at the glossary and then go back to where you were, which is analogous to a function call. Similarly, you might reread the same page a few times, which is analogous to a program loop. Even though you skip around a little, the order of pages has a logical and well-defined sequence.
Design Example: An Interrupt-Driven System
Let’s design a flowchart for a system that performs two independent tasks:
Task 1: The first task is to output a 20kHz square wave on
PORT-A(period is 50µs).Task 2: The second task is to read a value from
PORT-B, divide the value by 4, add 12, and output the result onPORT-D. This second task is repeated.
Here, PORT-A must change every 25µs. Therefore, we use a periodic interrupt for this “hard part” of the system.
The “firm” system is configured so that a hardware trigger will cause an interrupt every 25µs, and the software action (the ISR) will toggle PORT-A. The main program loop will be free to handle Task 2.
In the figure, the letters A to E specify software activities. The main program is executed in the background. Its execution is sequential and predictive (e.g., if C occurs, it will be after B and before D).
On the other hand, a hardware trigger causes the ISR to execute. The symbol > signifies an ISR software execution action in main program
In a processor like the ARM Cortex-M, the interrupt suspends background execution, executes the ISR in the foreground, and then resumes execution of the background from the instruction where it left off.
Let’s use an example where the main program executes B, C, and D over and over to search for prime numbers. In this example, a periodic timer causes the execution of ISR E every 2ms. Even though C comes after B and before D, the interrupt may inject an E between any two instructions of the background thread.
Being able to inject an E exactly every 2ms is how a real-time constraint is satisfied.
Example: Finding a Maximum Value
For parallel programming, consider the problem of finding the maximum value in a buffer. Finding the maximum value in the first half of the buffer and finding the maximum value in the second half of the buffer can be executed in parallel.





