Concurrency in Python: threads, processes and asyncio
You speed up waiting work with threads and asyncio and heavy computation with processes, and find and prevent bugs such as races and deadlocks.
Level 301 · 19 lessons · 18 practice labs
Lessons
Concepts and threads
- Concurrency and parallelism: what the GIL does
- I/O-bound and CPU-bound work
- Threads with threading
- Race conditions and Lock
- Producer-consumer with queue
- Deadlocks, Event and Semaphore
- Short task: download files in parallel and verify them
Processes
- Processes with multiprocessing
- Process pools and sharing data
- concurrent.futures: one interface, two pools
- Short task: hash thousands of files with a process pool
asyncio
- async and await: the event loop
- Tasks, gather and TaskGroup
- Timeouts and cancellation
- asyncio queues and concurrency limits
- Code that blocks the event loop, and to_thread
- Which model? Deciding by measuring
- Short task: probe a hundred services at once
Capstone
- Capstone: a bounded, cancellable job queue
Practice labs
- Limiting thumbnail jobs by pool size: Change a script that starts one thread per file to a ThreadPoolExecutor running at most four jobs at once and show the limit with a test.
- Collecting pool errors without losing them: In a pool where some jobs fail, gather each future's result or exception and return a breakdown of successful and failed jobs.
- Splitting waiting work across threads: Run five checks that each wait a second concurrently with threading instead of in turn and measure the time.
- Closing a race in ticket sales: Fix multi-threaded sales code that sells the same seat twice with a Lock so the stress test shows no overselling.
- Resolving a deadlock in mutual transfers: Fix code that hangs when two accounts send money to each other at once using a fixed lock order so the test does not time out.
- Limiting a printer pool with a semaphore: Ensure at most three print jobs run at once with a Semaphore and prove with a counter the limit is never exceeded.
- A job queue with producer and consumer: Build a job queue with queue.Queue, one producer and three consumers, closing the consumers cleanly when all jobs are done.
- Stopping a background worker with an Event: Stop a monitoring thread running in an endless loop properly with an Event rather than relying on the daemon flag.
- Protecting memory with a bounded queue: Fix a pipeline whose fast producer bloats memory by using a bounded queue and show peak memory stays flat.
- Giving CPU-heavy work to a process pool: Split a prime count into four parts with ProcessPoolExecutor and measure the time against the threaded version.
- Fixing a job that cannot be sent to the pool: Fix pool code that fails with a pickle error because of a lambda and an open file by using a module-level function and a path.
- Collecting results as they finish: Report jobs as they finish with as_completed, without one job's exception stopping the others, and list failures separately.
- Sharing a counter across processes correctly: Fix multi-process code that thinks it increments a global by using Value and Lock so the result is right every run.
- Querying fake services concurrently with gather: Turn code that awaits three fake price services in turn into concurrent calls with gather and cut the total time.
- A timeout and fallback for a slow call: Cancel a call exceeding one second with asyncio.timeout and return the cached value, without leaking the cancelled task.
- Limiting the number of concurrent tasks: Limit a thousand tasks to at most twenty running at once with asyncio.Semaphore and verify the peak with a test.
- Finding the call that blocks the event loop: Find the loop stalls caused by time.sleep and a synchronous file read in an async service, fix them with to_thread and measure latency.
- Cancelling sibling tasks on error with TaskGroup: Move tasks started with gather that keep running after a failure into a TaskGroup and collect the errors with except*.
About this course
You speed up waiting work with threads and asyncio and heavy computation with processes, and find and prevent bugs such as races and deadlocks.
What you will learn
- Concepts and threads
- Processes
- asyncio
- Capstone
Prerequisites
You should be comfortable with the level 101 and 201 topics of the same path.
Lab environment
Lessons run in your own Kali Linux terminal that opens in the browser. Nothing to install; the lab starts clean in every lesson.
Frequently asked questions
- Is this course right for my level?
- The course is at level 301; it assumes you know the topics of the lower-level courses.
- Is there anything to install?
- No. Every lesson runs in your own lab environment that opens in the browser.
- How do lessons work?
- Each lesson has 8 to 10 steps and a short quiz. Early steps show the command to type, later steps give less help; if two attempts fail, the solution opens.
- Can I try it for free?
- Yes. The first three lessons of every course are free.