Preemptive vs Non-Preemptive: Key Differences and Usage
preemptive multitasking, the operating system can interrupt and switch tasks at any time to ensure fair CPU use. In non-preemptive multitasking, a running task must voluntarily give up control before another task can run.Quick Comparison
Here is a quick side-by-side comparison of preemptive and non-preemptive multitasking in operating systems.
| Factor | Preemptive Multitasking | Non-Preemptive Multitasking |
|---|---|---|
| Task Switching | OS forcibly interrupts tasks | Tasks yield control voluntarily |
| CPU Control | OS controls CPU allocation | Task controls CPU until done or yields |
| Responsiveness | High, can switch quickly | Lower, depends on task cooperation |
| Complexity | More complex to implement | Simpler to implement |
| Risk of Starvation | Less risk due to forced switching | Higher risk if a task never yields |
| Example OS | Windows, Linux, macOS | Early Mac OS, MS-DOS |
Key Differences
Preemptive multitasking allows the operating system to interrupt a running task at any moment to switch to another task. This ensures that no single task can block the CPU for too long, improving system responsiveness and fairness. The OS uses a timer interrupt to decide when to switch tasks.
In contrast, non-preemptive multitasking relies on each task to voluntarily give up control of the CPU. The OS waits until the current task finishes or explicitly yields before running another task. This approach is simpler but can cause problems if a task never yields, leading to poor responsiveness.
Preemptive systems are more complex because they must handle interruptions safely, saving and restoring task states. Non-preemptive systems are easier to design but less robust in multitasking environments.
Code Comparison
This example shows how a simple task scheduler might work in a non-preemptive system where tasks must yield control explicitly.
class Task: def __init__(self, name): self.name = name self.completed = False def run(self): print(f"Running {self.name}") # Task does some work self.completed = True class Scheduler: def __init__(self, tasks): self.tasks = tasks def run(self): for task in self.tasks: if not task.completed: task.run() # Create tasks tasks = [Task("Task1"), Task("Task2")] # Run scheduler scheduler = Scheduler(tasks) scheduler.run()
Preemptive Equivalent
In a preemptive system, the OS can interrupt tasks. Here is a simplified Python example using threading and timer to simulate preemption.
import threading import time class Task(threading.Thread): def __init__(self, name): super().__init__() self.name = name self.running = True def run(self): while self.running: print(f"Running {self.name}") time.sleep(0.5) def stop(self): self.running = False # Create tasks task1 = Task("Task1") task2 = Task("Task2") task1.start() task2.start() # Simulate preemption by stopping tasks after some time threading.Timer(2, task1.stop).start() threading.Timer(3, task2.stop).start()
When to Use Which
Choose preemptive multitasking when you need a responsive system that fairly shares CPU time among many tasks, such as in modern desktop and server operating systems. It handles uncooperative tasks well and improves overall system stability.
Choose non-preemptive multitasking in simple or embedded systems where tasks are trusted to cooperate and system complexity must be minimal. It is easier to implement but less suitable for multitasking with many or unpredictable tasks.