liveBook · Manning

Preemption

As explained in Grokking Concurrency

Preemptive multitasking is an operating-system technique in which the system can suspend a running task without requiring its cooperation and schedule another task.

Imagine a computer playing an arcade game while it waits for a button press. Rather than leave the CPU idle, it can give another task a turn. Preemption lets the operating system make that switch without waiting for the running task to volunteer. section 6.7 section 6.8

In preemptive multitasking, a scheduler gives a ready task a short period of CPU time, called a time slice. When that slice expires, a timer interrupt lets the scheduler pause the task and run another. Rapid switching makes tasks appear to run together even when one CPU core executes only one at a time. section 6.7

An arcade-machine sketch

An arcade machine can alternate between checking for input, computing the game world, and rendering. A Python sketch uses a timer and an event to grant threads turns. It illustrates time sharing, but setting an event does not forcibly stop a thread already executing its function; the operating system supplies actual preemption. section 6.8 section 6.7

A threaded server

A socket server can start a Python thread for each client connection. The operating system schedules those threads, so one client’s wait for input need not prevent another client’s request from being handled. The programmer can write each handler as a straightforward sequence of operations. section 10.6

Preemption differs from cooperative multitasking: cooperative tasks must choose when to yield, while a preemptive scheduler can interrupt them. Neither approach makes switching free. Too many runnable tasks can waste time on context switching, and numerous system threads carry more overhead than user-level cooperative tasks. section 6.14 section 12.12

# Chapter 10/threaded_pizza_server.py 
from socket import socket, create_server
from threading import Thread

BUFFER_SIZE = 1024
ADDRESS = ("127.0.0.1", 12345)

class Handler(Thread):
    def __init__(self, conn: socket):
        super().__init__()
        self.conn = conn

    def run(self) -> None:
        print(f"Connected to {self.conn.getpeername()}")
        try:
            while True:
                data = self.conn.recv(BUFFER_SIZE)
                if not data:
                    break
                try:
                    order = int(data.decode())
                    response = f"Thank you for ordering {order} pizzas!\n"
                except ValueError:
                    response = "Wrong number of pizzas, please try again\n"
                print(f"Sending message to {self.conn.getpeername()}")
                self.conn.send(response.encode())
        finally:
            print(f"Connection with {self.conn.getpeername()} "
                  f"has been closed")
            self.conn.close()

class Server:
    def __init__(self) -> None:
        try:
            print(f"Starting up at: {ADDRESS}")
            self.server_socket = create_server(ADDRESS)
        except OSError:
            self.server_socket.close()
            print("\nServer stopped.")

    def start(self) -> None:
        print("Server listening for incoming connections")
        try:
            while True:                                              #1
                conn, address = self.server_socket.accept()          #1
                print(f"Client connection request from {address}")   #1
                thread = Handler(conn)                               #1
                thread.start()                                       #1
        finally:
            self.server_socket.close()
            print("\nServer stopped.")

if __name__ == "__main__":
    server = Server()
    server.start()

The Python socket-server listing shows a separate thread handling each client while scheduling remains the operating system’s job. section 10.6

How Grokking Concurrency covers it

How deep it goes
Explains time slices, scheduler interrupts, and task switching, then sketches the behavior in Python rather than implementing an operating-system scheduler.
Examples
Illustrates timer-driven arcade tasks and a Python socket server with one thread per client.
What it assumes
Assumes familiarity with programs; introduces CPU scheduling, time slices, and interrupts before illustrating threads in Python.
What you can do after
Apply it via Python threads to serve concurrent clients while leaving thread scheduling to the operating system.
How it connects
Connects CPU scheduling, timer interrupts, time sharing, threading, and cooperative multitasking.

Also explained in

Compare how 5 books explain preemption →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Grokking Concurrency. The section links point to the parts of the book it draws on.