Concurrency structures multiple executions so they make progress during overlapping periods without necessarily running simultaneously. Parallelism runs multiple threads at the same time; concurrency may instead interleave work when fewer processors are available. A practical example is fetching email while displaying a message. section 8.4
Use concurrency to divide work while preserving the surrounding application:
- Identify a bottleneck and isolate work that can run independently. section 8.4
- Submit tasks to a ThreadPoolExecutor and coordinate them with asyncio.gather. section 8.4
- When Python's GIL blocks parallel execution, expose a Rust function through PyO3 and release the GIL with allow_threads. section 8.6
Concurrent Mandelbrot generation
Eight Mandelbrot images are submitted through a four-worker thread pool. Replacing the Python implementation with Rust reduces measured time from 46 to 23 seconds; releasing the GIL enables true concurrency and reduces it to 6 seconds. section 8.4 section 8.6
Figure 8.5 Within our box, we do not know how many crushers we have, and we have no guarantee of the order. Adding more crushers adds more throughput.
Visualizes concurrency as multiple black-box crushers: more crushers increase throughput, while internal count and output order remain unspecified. section 8.4






















