Imagine a plate spinner moving from plate to plate, keeping each one in motion. Multitasking works similarly: a runtime manages several tasks so each can make progress over time. On one processor core, their instructions still execute serially; rapid switching makes the tasks appear to run at once. Parallel execution requires additional processing resources. section 6.6 section 6.11
A task might wait for a button press while other work is ready to run. Rather than leave the processor idle during that wait, a runtime can give another task a turn. Processes and threads let applications express these separate activities; the operating system schedules them rather than asking the CPU to understand either abstraction. section 6.8 section 6.11
One-core arcade machine
An arcade game needs to read player input, update its world, and render the next screen. Merely creating three threads does not make those jobs run in parallel on a single core: an endless input loop can prevent the other jobs from advancing. A time-sharing design instead gives threads turns. A timer marks intervals, and the runtime switches tasks often enough that play appears continuous. section 6.5 section 6.8
# Chapter 6/arcade_machine_multitasking.py
import typing as T
from threading import Thread, Timer, Event
from pacman import get_user_input, compute_game_world, render_next_screen
processor_free = Event()
processor_free.set()
TIME_SLICE = 0.5 #1
class Task(Thread):
def __init__(self, func: T.Callable[..., None]):
super().__init__()
self.func = func
def run(self) -> None:
while True:
processor_free.wait()
processor_free.clear()
self.func()
class InterruptService(Timer):
def __init__(self):
super().__init__(TIME_SLICE, lambda: None)
def run(self):
while not self.finished.wait(self.interval): #2
print("Tick!") #2
processor_free.set() #2
def arcade_machine() -> None:
get_user_input_task = Task(get_user_input)
compute_game_world_task = Task(compute_game_world)
render_next_screen_task = Task(render_next_screen)
InterruptService().start()
get_user_input_task.start()
compute_game_world_task.start()
render_next_screen_task.start()
if __name__ == "__main__":
arcade_machine()
The Python arcade listing illustrates a timer and an Event coordinating turns between thread-based tasks; it is a teaching model of time-sharing, not a substitute for an operating-system scheduler. section 6.8
The practical distinction is between progress and simultaneous execution:
- Concurrency means multiple tasks are in progress over a period of time; on one core, they can achieve this through interleaving. section 6.6 section 6.8
- Parallelism means tasks execute at the same time on suitable hardware. A multiprocessor system can combine parallel execution with multitasking. section 6.11 section 6.6
- Switching tasks has overhead, and coordinating shared resources needs care. Runtime scheduling simplifies application structure, but does not guarantee a speedup. section 6.10 section 13.1
