On this page
Python Concurrency: When Threads Are Faster Than They Look
pythonconcurrency
A practical look at threads, async and the GIL in CPython.
The GIL often scares people away from threads in Python. In practice, threads win big whenever your work spends time waiting — on I/O, sockets, or a sleeping dependency — rather than spinning the CPU.
The rule of thumb
- I/O-bound work → threads or async both work well; threads are simpler to reason about.
- CPU-bound work → you need processes (
ProcessPoolExecutor) or a native extension that releases the GIL.
Small example
import concurrent.futures
import time
import urllib.request
URLS = ["https://example.com"] * 8
def fetch(url: str) -> int:
with urllib.request.urlopen(url, timeout=10) as resp:
return resp.status
def main() -> None:
with concurrent.futures.ThreadPoolExecutor() as pool:
statuses = list(pool.map(fetch, URLS))
print(statuses)
if __name__ == "__main__":
main()
Takeaways
- Start with
ThreadPoolExecutor; it hides the plumbing. - Set explicit timeouts; blocked threads are the real problem.
- Measure. The GIL is rarely the bottleneck people imagine.