A practical lab for understanding Python concurrency in the context of real workloads.
This lab builds on object_model_lab and focuses on:
- Threads and the GIL
- Race conditions and shared state
- Locks and thread-safe patterns
- Producer–consumer pipelines
- Thread pools
- Processes vs threads
asynciobasics and async pipelines- Comparing async vs threads on FX-like workloads
Each file is a standalone experiment; together they form a coherent mental model.
- Create threads using
threading.Thread - Show interleaved output
- Demonstrate join and main-thread blocking
- Shared counter incremented by multiple threads
- Demonstrates race conditions and non-deterministic results
- Protect shared state using
LockandRLock - Show correct increments and discuss cost
- Use
queue.Queuewith producer/consumer threads - Graceful shutdown using sentinel values
- Use
concurrent.futures.ThreadPoolExecutor - Map tasks across threads
- Discuss when to use pools vs manual threads
- Use
ProcessPoolExecutorvsThreadPoolExecutor - Compare for CPU-bound vs IO-bound workloads
- Introduce coroutines,
async/await - Run multiple coroutines concurrently
- Show difference between sequential vs concurrent async
- Async producer–consumer using
asyncio.Queue - Demonstrate backpressure and cooperative multitasking
- Simulated FX quote/order workload
- Compare threads vs asyncio for IO-bound tasks
- Show where async shines and where it doesn’t
- Simulated CPU-bound and IO-bound tasks
- Reused across the lab for consistent comparison
- Timing helpers for measuring execution time
By the end of this lab, you should be able to:
-
Explain Python’s GIL and its impact
-
Choose between threads, processes, and asyncio
-
Build thread-safe and async-safe producer–consumer pipelines
-
Reason about state mutation under concurrency
-
Apply these patterns to FX-style microservices
From the project root:
python 01_threads_basics.py
python 02_race_conditions.py
...
python 09_asyncio_vs_threads_fx_demo.py