Unit 6.1 · about 5 h
35 / 324Work types, threads, processes, async tasks and the GIL
Concurrency overlaps work; it does not automatically make that work parallel. A workload and build-specific experiment is a better design input than a blanket claim about Python threads.
- explain
- measure
You can now
- Choose threads, processes or async tasks from CPU work, I/O waits, shared state and cancellation needs.
- Measure CPU-bound thread scaling on default and free-threaded CPython 3.14 builds without assuming their GIL state.
- Identify a blocking call that prevents other event-loop tasks from progressing.
Covers
- CPU-bound, I/O-bound, stateful and cancellation-sensitive work
- Threads, processes and asynchronous tasks
- Python GIL implications and extension-code release boundaries
Study, in this order
3 steps
- Concurrency models and Python workload choicesAlgoMaster Concurrency
Concurrency Models; then Threading vs Multiprocessing, Key Differences and When to Use Threading/Multiprocessing; Asyncio Fundamentals, The Event Loop and Common async/await Mistakes
Depth · Use the models as a first pass. Check build-specific GIL and process-start claims against the 3.14 references below.
- Free-threaded CPython is a separate buildPython concurrency documentation
Identifying free-threaded Python; The global interpreter lock in free-threaded Python; Thread safety; Performance characteristics
- Process startup is part of the experimentPython concurrency documentation
Contexts and start methods:
spawn,forkandforkserver, including the 3.14 default changes and safe importing of the main module
Focus on
- Default CPython 3.14 has a GIL. Pure Python CPU loops do not gain multicore parallelism from its threads; I/O waits and native code that releases the GIL are different cases.
- Free-threaded builds can execute Python threads in parallel. Record
sys.version,sysconfig.get_config_var('Py_GIL_DISABLED')andsys._is_gil_enabled()after imports; an unsupported extension can re-enable the GIL. - Async tasks share an event-loop thread and yield cooperatively. A synchronous CPU loop or
time.sleepinside a task stalls that loop. - Processes isolate state but pay startup and serialization costs. In 3.14
forkis no longer the default anywhere: Linux and other POSIX platforms default toforkserver, while macOS and Windows usespawn. Select and report the context explicitly.
Reproduce closed-book
n*(n-1)//2. Compare serial, four threads and four spawned processes on both CPython 3.14 builds. Use one warm-up and five measured trials per mode, include pool startup and save raw times, CPU, full version and GIL state. A reference run on Intel Core i9-12900K gave median seconds serial/threads/processes of 0.304/0.321/0.153 on 3.14.4 default and 0.315/0.088/0.156 on 3.14.6 free-threaded with GIL disabled. These are observations, not required speedups. Also schedule a heartbeat around a synchronous blocking call, then replace it with an awaited nonblocking operation and explain the missing versus advancing heartbeat.Traps
- Treating a free-threaded executable name as proof that the GIL stayed disabled after imports.
- Replacing the loop with
sum(range(n))and calling it the same pure Python workload, or comparing different total work across worker counts. - Calling
asyncio.to_threada CPU-parallelism guarantee on the default build. It keeps blocking I/O off the loop but does not remove the GIL.
Practice
The section mini-project covers this: matched-work concurrency comparisons and an event-loop blocking fault in the bounded worker pipeline.
Self-test
-
Which model fits many socket waits with little CPU work?
Async tasks fit when the client APIs are nonblocking. Threads fit blocking clients. Bound either model's in-flight work. -
Why might processes lose on tiny jobs?
Startup, serialization and communication can cost more than the CPU work they parallelize. -
Does no GIL mean no need for locks?
No. Internal container protection does not make a multi-step application invariant atomic. -
Do these two build measurements isolate the cost of removing the GIL?
No. Patch versions and compilers differ too. Compare scaling within each build; do not attribute every cross-build timing difference to the GIL.
Completion criterion
Stop when
You have correctness-checked raw timings from both builds and can justify a model for CPU work, blocking I/O and cancellation-sensitive work without claiming a universal speedup.
Mark passed, then open 6.2 Only mark this when you can do it without notes.Leads to
- 6.2 Synchronization primitives and concurrency hazards Protect the state your chosen model shares.
Resources for this unit
- AlgoMaster Concurrency Study Concurrency 101; the Python units on threads, the GIL, multiprocessing, asyncio and concurrent.futures; synchronization primitives; concurrency challenges; locking strategies; and concurrency patterns. Then solve Bounded Buffer, Thread-Safe Blocking Queue, Thread-Safe Rate Limiter and Task Scheduler with Dependencies.
- Python concurrency documentation Use asyncio task groups, cancellation, queues and timeouts; concurrent.futures; multiprocessing; threading; and signal documentation to verify lifecycle and failure behavior.
Also in this section
- High Performance Python, 3rd edition Read Chapter 2 Profiling to Find Bottlenecks, Chapter 9 Asynchronous I/O, Chapter 10 The multiprocessing Module and Chapter 12 Using Less RAM, and use the companion code (https://github.com/mynameisfiber/high_performance_python_3e) as experiment starting points. Change the workloads and measure the regimes where concurrency helps, loses or only moves the bottleneck.
- Operating Systems: Three Easy Pieces - concurrency Read Chapters 26-33, from threads through event-based concurrency, for locks, condition variables, semaphores, concurrency bugs and event loops. Do selected homework only for a weak mechanism.
- Docker guides Use the Python container guide and build best practices to run as non-root, pin dependencies, handle signals and test CPU and memory limits.
- OpenTelemetry Python instrumentation Manually instrument one request so its structured logs, one low-cardinality metric and one trace share a correlation identity. Use the telemetry to locate one seeded fault.
- Algorithmica - Getting Accurate Results Use Measuring the Right Thing and Reducing Noise for benchmark design: input distribution, latency versus throughput, warm-up, frequency scaling and pinning. The examples are C++; the checks transfer to Python.
- Timeouts, retries, and backoff with jitter Read Timeouts, Retries and backoff, and Jitter for deadline scope, idempotency, one retry owner and bounded retry load. Interpret the five-layer example as three total attempts per layer, not three retries plus the initial attempt.
- Site Reliability Engineering - overload and troubleshooting Read Chapter 21, The Pitfalls of Queries per Second, Client-Side Throttling, Utilization Signals, Handling Overload Errors and Conclusions. Read Chapter 12, Triage, Examine, Diagnose, Test and Treat, and Making Troubleshooting Easier for a service runbook. Skip datacenter load-balancing implementation details.
- Liveness, Readiness, and Startup Probes Read Liveness probe, Readiness probe and Startup probe for the different health decisions and failure actions. Apply the contracts to a local service; do not install Kubernetes or configure a cluster.