What is the Global Interpreter Lock, and how do you achieve concurrency in Python?
The GIL is a mutex in CPython allowing only one thread to execute Python bytecode at a time. It exists because CPython's memory management uses reference counting, which is not thread-safe. The consequence is that threads do not give you parallel CPU execution.
Critically, the GIL is released during I/O. That is what makes the choice straightforward:
- I/O-bound work — network calls, disk, database queries. Use
threadingorasyncio. While one thread waits on a socket, others run, so you get real concurrency.asyncioscales further because it does not need a thread per task. - CPU-bound work — number crunching, image processing, parsing. Use
multiprocessing, which runs separate interpreter processes each with their own GIL. The cost is that data must be pickled between processes. - Numerical work — NumPy, Pandas, and similar libraries release the GIL inside their C routines, so they already parallelise without you doing anything.
Note: Mention that PEP 703 introduces an optional free-threaded build in Python 3.13, making the GIL removable. It is experimental, but knowing it exists shows you follow the language rather than repeating a decade-old summary.





