
本文澄清一个常见误解:python 的全局解释器锁(gil)本身并不实现线程排队、唤醒或调度逻辑;线程的就绪、休眠与恢复完全由底层操作系统内核负责,gil 仅控制同一时刻只有一个线程执行 python 字节码。
本文澄清一个常见误解:python 的全局解释器锁(gil)本身并不实现线程排队、唤醒或调度逻辑;线程的就绪、休眠与恢复完全由底层操作系统内核负责,gil 仅控制同一时刻只有一个线程执行 python 字节码。
在你提供的多线程示例中,5 个 Thread 对象几乎同时启动,并依次调用 time.sleep(1)。此时,每个线程会:
-
主动让出 CPU:
time.sleep()是一个阻塞系统调用(如nanosleep),它会将当前线程状态置为TASK_INTERRUPTIBLE(Linux)或等效状态,交由 OS 调度器管理; -
脱离 Python 运行时控制:GIL 在进入
sleep前已被释放(CPython 源码中floatsleep()会显式PyThreadState_Swap(NULL)并pthread_cond_wait),因此 GIL 不参与后续唤醒决策; - 由 OS 决定唤醒顺序:当睡眠超时后,OS 调度器将线程重新标记为“可运行”,并根据其调度策略(如 CFS 的虚拟运行时间 vruntime)、优先级、CPU 亲和性、缓存局部性、抢占时机等,决定哪个线程最先获得 CPU 时间片——该顺序与线程创建/启动顺序无关,也与 GIL 无关。
这就是为何你观察到 Starting thread N 总是按 0→4 顺序打印(主线程串行启动子线程,且启动开销极小),而 Ending thread N 的顺序却每次不同:
import time
from threading import Thread
def do_work(thread_number):
print(f"Starting thread {thread_number}") # ✅ 同步发生(主线程串行 start())
time.sleep(1) # ⚠️ 此刻线程交由 OS 管理
print(f"Ending thread {thread_number}") # ❓ 异步竞争输出(取决于 OS 调度 + I/O 缓冲)
for i in range(5):
t = Thread(target=do_work, args=(i,))
t.start()
? 关键事实:
- GIL 不维护“就绪队列”,也不对被中断/休眠的线程进行 FIFO 或 LIFO 排队;
threading模块中的Lock、Condition等同步原语底层依赖 OS 互斥量/条件变量,其等待队列由 OS 实现;time.sleep()、socket.recv()、queue.get()等阻塞操作均会释放 GIL 并委托给 OS;- 打印乱序还受
sys.stdout缓冲影响(多线程写同一 stdout 可能交错),建议使用print(..., flush=True)或threading.Lock()保护输出。
✅ 正确理解模型:
Python 程序 → CPython 解释器(含 GIL) → OS 线程库(pthread) → 内核调度器(如 Linux CFS)
↘
真正的就绪队列 & 时间片分配
因此,不要试图从 GIL 行为推断线程执行顺序——那是操作系统的职责。若需确定性顺序,请显式同步(如 thread.join()、queue.Queue 或 threading.Barrier),而非依赖调度巧合。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











