Why Does Python’s GIL Slow Down Multithreading? A Deep Dive into Its Mechanics
This article explains the Global Interpreter Lock (GIL) in CPython, how it serializes thread execution on both single‑ and multi‑core CPUs, why it hampers parallelism, and what code patterns are safe or unsafe under its coarse‑grained locking.
GIL (Global Interpreter Lock) is not a Python‑specific feature but a concept introduced in the CPython implementation. The official definition states:
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread‑safe.
Thus, GIL is a mutex that blocks concurrent execution of Python bytecode, reducing efficiency. Understanding its necessity requires knowledge of CPython’s thread‑unsafe memory management.
On a single‑core CPU, only one thread can run at any moment; threads release the GIL when they encounter I/O operations or when a timer tick expires. The following diagram illustrates this behavior:
When a thread releases the GIL due to I/O, the other two threads compete for the lock while the I/O‑bound thread does not participate. If the release is caused by a timer tick, all three threads compete, and the original thread may reacquire the lock immediately. The timer tick defines the maximum execution time for a thread before the GIL is automatically released.
On multi‑core CPUs, the global nature of the GIL prevents threads on different cores from running simultaneously, causing idle CPUs and dramatically reduced performance. Python 3.x mitigates this by automatically adjusting thread priorities to improve multithreaded efficiency.
The GIL’s purpose is to provide a coarse‑grained thread‑safety guarantee, but it does not make every operation atomic. The following example demonstrates a race condition:
def add():
global n
for i in range(10**1000):
n = n + 1
def sub():
global n
for i in range(10**1000):
n = n - 1
n = 0
import threading
a = threading.Thread(target=add)
b = threading.Thread(target=sub)
a.start()
b.start()
a.join()
b.join()
print(n)Although the program adds and subtracts the same amount, the printed result is often non‑zero because the statement n = n + 1 is not atomic. The compiled bytecode shows the operation is split into four steps:
19 LOAD_GLOBAL 1 (n)
22 LOAD_CONST 3 (1)
25 BINARY_ADD
26 STORE_GLOBAL 1 (n)During these steps, the thread may release the GIL, allowing another thread to interleave and corrupt the result. Some operations are atomic and safe under the GIL, such as list.sort, which compiles to a single bytecode instruction ( CALL_METHOD 0) and therefore cannot be pre‑empted.
Summary
For I/O‑bound applications, multithreading performs similarly to multiprocessing because I/O operations release the GIL, allowing other threads to run; the lower communication overhead makes multithreading preferable.
For CPU‑bound applications, multithreading is at a disadvantage; using multiprocessing or asynchronous coroutines yields better performance.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
