Often, there's a misconception about the Global Interpreter Lock (GIL) in Python, where it's believed that the
Python interpreter only permits one thread to run at any given time. Infact, Python Interpreter allows threads
running in parallel when GIL is released in a function. For instance, functions like time.sleep
leverage I/O
blocking within Py_BEGIN_ALLOW_THREADS
and Py_END_ALLOW_THREADS
, enabling the Python interpreter to handle
multiple threads simultaneously in such cases.
reference:
The provided reference demonstrates how Python allows sleep operations to run in parallel:
import threading
import time
s = time.time()
t1 = threading.Thread(target=time.sleep, args=(5,))
t2 = threading.Thread(target=time.sleep, args=(5,))
t1.start()
t2.start()
t1.join()
t2.join()
e = time.time()
print(f"duration: {e - s}")
# output:
# python test.py
# duration: 5.005425930023193
However, if a function doesn't release the GIL, Python restricts execution to one thread at a time. Consider the following example:
import threading
import time
import sys
def f(x):
for i in range(x):
...
n = int(sys.argv[1])
threads = [threading.Thread(target=f, args=(100000000,)) for t in range(n)]
s = time.time()
[t.start() for t in threads]
[t.join() for t in threads]
e = time.time()
print(f"duration: {e - s}")
# output
# $ python test.py 1
# duration: 0.7972819805145264
# $ python test.py 2
# duration: 1.5789308547973633