GIL only affects CPU-bound tasks. I/O-bound tasks are not impacted.
CPU-bound (GIL limits parallelism):
1import threading2import time34def cpu_heavy():5 total = 06 for i in range(10_000_000):7 total += i8 return total910# Slower than single-threaded!11start = time.time()12t1 = threading.Thread(target=cpu_heavy)13t2 = threading.Thread(target=cpu_heavy)14t1.start(); t2.start()15t1.join(); t2.join()16print(time.time() - start) # ~6s (not ~3s)
I/O-bound (GIL released during I/O):
1import threading2import urllib.request34def fetch(url):5 return urllib.request.urlopen(url).read()67# Much faster with threads8threads = [9 threading.Thread(target=fetch, args=(url,))10 for url in urls11]
Rules of thumb:
multiprocessing.asyncio or threading.