
The GIL: Python has a Global Interpreter Lock — only one thread executes Python bytecode at a time. Threading helps with I/O-bound tasks, not CPU-bound.
Threading (I/O-bound tasks):
import threading
def download(url):
# simulate download
print(f"Downloading {url}")
threads = []
for url in urls:
t = threading.Thread(target=download, args=(url,))
threads.append(t)
t.start()
for t in threads:
t.join()asyncio (modern I/O concurrency):
import asyncio
async def fetch(url):
# async code here
await asyncio.sleep(1) # non-blocking wait
return f"Data from {url}"
async def main():
results = await asyncio.gather(
fetch("url1"),
fetch("url2")
)
asyncio.run(main())multiprocessing (CPU-bound): Bypasses the GIL. Use for heavy computation.
Reference:
TaskLoco™ — The Sticky Note GOAT