You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于Python中GIL的线程就绪队列调度机制及线程结束顺序差异的技术问询

Python中GIL的线程就绪队列调度机制及线程结束顺序差异的技术问询

Context

我正在执行一个创建5个线程的Python程序,代码如下:

import time
from threading import Thread


def do_work(thread_number):
    print(f"Starting thread {thread_number}")
    time.sleep(1)
    print(f"Ending thread {thread_number}")


for i in range(5):
    t = Thread(target=do_work, args=(i,))
    t.start()

我多次运行程序后得到了以下不同的输出:

Output 1

Starting thread 0
Starting thread 1
Starting thread 2
Starting thread 3
Starting thread 4
Ending thread 1
Ending thread 0
Ending thread 4
Ending thread 3
Ending thread 2

Output 2

Starting thread 0
Starting thread 1
Starting thread 2
Starting thread 3
Starting thread 4
Ending thread 4
Ending thread 2
Ending thread 3
Ending thread 0
Ending thread 1

Output 3

Starting thread 0
Starting thread 1
Starting thread 2
Starting thread 3
Starting thread 4
Ending thread 4
Ending thread 1
Ending thread 3
Ending thread 2
Ending thread 0

My Question

从输出中可以明显看出,我猜测GIL似乎把被中断的线程放回了等待队列的末尾。这也是为什么每次都是所有Started Thread <thread number>先打印出来,之后才会打印Ending Thread <thread number>。

但既然线程是按顺序启动执行的(先0号,再1号,然后2号……直到4号),而且如果被中断的线程被放到就绪队列的末尾,为什么它们的结束顺序不是和启动顺序相反呢?

究竟是哪些因素导致了这种顺序的变化?


我的解答

首先得纠正你一个关键的误解:CPython的GIL并没有维护一个简单的“等待队列”,也不会把被中断的线程固定放到队列末尾。线程的调度逻辑比你想象的要复杂,是GIL的释放时机和操作系统的线程调度器共同作用的结果:

1. 为什么所有“Starting”都会先全部打印出来?

这和time.sleep(1)的行为直接相关:当Python线程执行到time.sleep()时,会主动释放GIL,然后进入休眠状态。你的主线程启动0号线程后,0号线程打印“Starting”就调用sleep,主动释放GIL;此时主线程可以继续启动1号线程,重复这个过程——直到所有5个线程都完成了“Starting”的打印并进入休眠。这部分顺序固定,完全是因为sleep触发了主动的GIL释放,让主线程能快速启动所有子线程。

2. 为什么结束顺序每次都不一样?

当sleep的1秒结束后,所有休眠的线程会被操作系统唤醒,然后它们需要重新竞争GIL才能继续执行剩下的print("Ending...")。这时候的调度权完全在操作系统的线程调度器手里,和GIL自己的规则无关:

  • 操作系统的线程调度器会根据线程的优先级、当前系统的负载、时间片分配策略(比如轮转调度、优先级调度)等多种因素,选择下一个获得CPU时间片并拿到GIL的线程。
  • CPython在3.2版本之后,GIL的释放是通过定时器触发的(默认每100个字节码指令释放一次),但线程唤醒后的竞争没有固定顺序,也不是简单的“先中断的放最后”。
  • 另外,不同线程从唤醒到实际拿到GIL的时间也可能有细微差异,这也会影响最终的结束顺序。

3. 关于你提到的“中断线程放最后”的误区

你以为GIL会把被中断的线程放到队列末尾,但实际上CPython并没有为等待GIL的线程维护一个有序队列。当一个线程释放GIL后,所有等待的线程会进入竞争状态,操作系统会选择其中一个线程来执行——这个选择不是FIFO,也不是固定把之前的线程排到最后,完全由操作系统的调度策略决定。

总结一下:你看到的结束顺序差异,本质是操作系统线程调度器的“随机”选择导致的,和GIL本身的队列无关。GIL只是保证同一时刻只有一个Python字节码执行流,但线程的调度最终还是由操作系统说了算。


备注:内容来源于stack exchange,提问作者Deepak Tatyaji Ahire

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 18:03:10