Preemptive multitasking lets the operating system decide when to switch between running tasks. Time slicing divides execution, and each forced switch is called preemption. section 1.3.4
The operating system manages the switching through multiple threads or processes. This creates concurrency even on a single-core CPU, although only one task executes at any instant. section 1.3.4 section 1.3.3
Cooperative multitasking differs by requiring application code to identify safe points where other tasks may run. Preemptive switching is more automatic, but context switches consume resources and may occur at less useful moments. section 1.3.4 section 1.3.5
Process-pool execution
A ProcessPoolExecutor distributes counting tasks among processes. Small counts finish quickly, while a count to 100000000 completes later, demonstrating independently scheduled work and asynchronous result availability. section 6.3.1
Listing 6.4 Process pool executors
import time
from concurrent.futures import ProcessPoolExecutor
def count(count_to: int) -> int:
start = time.time()
counter = 0
while counter < count_to:
counter = counter + 1
end = time.time()
print(f'Finished counting to {count_to} in {end - start}')
return counter
if __name__ == "__main__":
with ProcessPoolExecutor() as process_pool:
numbers = [1, 3, 5, 22, 100000000]
for result in process_pool.map(count, numbers):
print(result)
Runnable process-pool example showing task distribution, timing, and results from counts of different sizes. section 6.3.1




