持有GIL的线程什么时候释放GIL?
先说结论:如果未持有GIL的线程太久没有获取GIL,已经持有GIL的线程就会释放锁。
在ceval.c#1237行中可以看到:ceval->gil_drop_request 被标记时,会释放锁。
main_loop:
for (;;) {
...
if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) { // 其他线程要求当前线程释放锁
/* Give another thread a chance */
if (_PyThreadState_Swap(&runtime->gilstate, NULL) != tstate) {
Py_FatalError("ceval: tstate mix-up");
}
drop_gil(ceval, tstate); // 当前线程释放锁
/* Other threads may run now */
take_gil(ceval, tstate); // 当前线程尝试再获取锁
...
}
eval->gil_drop_request 什么时候被标记呢?
看下面的代码可以得出结论:如果 未持有GIL的线程 超过了等待时间 且 GIL是锁住的状态 且 GIL的持有者没有变更过,就会标记 ceval->gil_drop_request变量。
static void take_gil(PyThreadState *tstate)
{
...
while (_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked)) { // 只要 gil 是锁住的状态, 进入这个循环
int timed_out = 0;
unsigned long saved_switchnum;
saved_switchnum = _PyRuntime.ceval.gil.switch_number; // 保存当前持有GIL的身份
// 释放 gil.mutex, 并在以下两种条件下唤醒
// 1. 等待 INTERVAL 微秒(默认 5000)
// 2. 还没有等待到 5000 微秒但是收到了 gil.cond 的信号
COND_TIMED_WAIT(_PyRuntime.ceval.gil.cond, _PyRuntime.ceval.gil.mutex,
INTERVAL, timed_out);
// 如果超过了等待时间 && GIL是锁住的状态 && GIL的持有者没有变更过
if (timed_out &&
_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked) &&
_PyRuntime.ceval.gil.switch_number == saved_switchnum) {
SET_GIL_DROP_REQUEST(); // 把 gil_drop_request 值设为 1, 持有GIL的线程看到这个值的时候, 会尝试释放GIL
}
}
...
}
所以,当未持有GIL的线程太久没有获取GIL了,就会设置一个全局变量。当前持有GIL的线程看到全局变量后,就知道自己要释放GIL。