- Simple Tasks
- Multiple Tasks
- Communicating Tasks
- Communicating Values
- Synchronising Tasks
- Buffered Communication
- Multiple Senders
- Conditional Tasks
This Arduino project demonstrates how simple cooperative multitasking can be implemented in a largely processor independent fashion in C++. It also provides a vehicle for leaning about and experimenting with simple concurrent systems. This can be done without a detailed understanding of the underlying implementation.
Multitasking operating systems allow single processors, which can only do one thing at a time, to appear to perform multiple tasks simultaneously, e.g. print a document while responding to mouse clicks in a web browser. Even when computers actually have multiple processors (sometimes called cores), there are generally more tasks to perform than processors to perform them, so that each processor is still required to run multiple tasks.
Even outside of operating systems, allowing software systems to be split into multiple independent, or cooperating tasks, often simplifies their organisation. Imagine how complicated it would be to write a program which had to be a print spooler, a web browser and a word processor all at the same time. It is much easier to write three independent programs that can be executed as separate, possibly interacting, tasks even if only one processor is available.
So how is this done? Suppose that we have two tasks to perform on one processor. We can arrange that the processor executes a small part of task one, and then a small part of task two, before returning to the next part of task one. If this is repeated rapidly, it will create the illusion of the two tasks executing simultaneously. The nature of the tasks may provide a means of achieving this, e.g. both of the tasks may periodically have to wait for a physical device to perform some operation, such as waiting for keyboard input or a disk transfer, before it can continue. As soon as one task becomes delayed, the other task can resume execution until it too is delayed. This will not work very well if one or more tasks spend long periods performing computation without interacting with physical devices. Such task would disrupt the smooth switching between tasks.
To overcome this problem, the task management software, usually part of an operating kernel, can impose an upper limit on the time a task can execute for before it must give way to allow another task to use the processor. The maximum time is often called a "time slice" and the technique is known as "pre-emptive multitasking". The time slicing and the delaying of tasks waiting for devices make use of processor interrupts. A task can initiate an operation on an external device and then request that it be suspended until the operation is complete. The device generates an interrupt when it has finished the operation allowing the task to be resumed. In the meantime the processor can execute other tasks.
Pre-emptive multitasking can be implemented by using a hardware timer which interrupts the current task when its time slice expires. The multitasking system then chooses another task that is ready for execution, i.e. not waiting for an external device. Tasks that are ready for execution are often organised in a queue. When a task comes to the end of its time slice without being delayed, it is put to the back of the queue and the task at the front of the queue is executed. Thus, even if there are many tasks performing computations without waiting for devices, the system will execute them each a bit a a time in order giving the illusion of smooth multi tasking. More complex systems may allow some tasks to be identified as more important than others. In this case higher priority tasks will be executed in preference to lower priority task.
This very brief introduction to the subject of multitasking should be sufficient to allow the details of a simple Arduino based multitasking system written in C++ to be described.
Next: Describing Simple Tasks