Concurrency & Delay in Python
Deep dive into how Python handles delays, asynchronous execution, concurrency, parallelism, multiprocessing, threading, asyncio, coordination patterns, and thread safety in real-world backend systems.
Waiting & Asynchronous Execution
Python supports both blocking delays and non-blocking asynchronous execution. Understanding the difference is critical for backend performance.
1. Blocking Delay (time.sleep)
Pauses the entire thread.
2. Non-Blocking Delay (asyncio.sleep)
Uses event loop.
Coordinating Asynchronous Operations
asyncio provides tools to coordinate multiple asynchronous operations.
1. Wait for Multiple Tasks (asyncio.gather)
Equivalent to Promise.all.
2. Wait for First Completed (asyncio.wait)
Similar to Promise.race.
Passing Data Between Async & Sync Code
Async functions return coroutine objects which must be awaited.
1. Returning Data from Async Function
Using await.
Timers & Repeated Execution
Python can schedule repeated tasks using loops or threading timers.
1. Delayed Execution (threading.Timer)
Run once after delay.
Measuring Execution Time
Profiling helps optimize concurrent programs.
1. High Precision Timing
Using time.perf_counter.
Threading
Threads allow concurrent I/O operations but are limited by the GIL for CPU work.
1. Basic Thread Example
Using threading.Thread.
Multiprocessing
Multiprocessing enables true CPU parallelism by bypassing the GIL.
1. Basic Process Example
Using multiprocessing.Process.
Race Condition in Python Threads (GIL Behavior)
Python (CPython) uses a Global Interpreter Lock (GIL) which allows only one thread to execute Python bytecode at a time. This can give the illusion that shared state updates are safe, but they are not guaranteed to be atomic.
1. Counter Example (Looks Safe)
Two threads incrementing shared counter.
2. Why It Often Prints 200000
Understanding GIL scheduling.
3. Forcing the Race Condition
Introduce artificial context switching.
4. Fixing It with threading.Lock
Protect critical section.
Global Interpreter Lock (GIL)
The Global Interpreter Lock (GIL) is a mechanism in CPython that ensures only one thread executes Python bytecode at a time within a single process.
1. Why the GIL Exists
Understanding the design trade-off.