Vitaly Koryakin
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

  1. Start with ThreadPoolExecutor; it hides the plumbing.
  2. Set explicit timeouts; blocked threads are the real problem.
  3. Measure. The GIL is rarely the bottleneck people imagine.