Python中线程如何检测IO操作并自动释放GIL锁给其他线程执行?

Python中线程如何检测IO操作并自动释放GIL锁给其他线程执行?

8 回复

底层 IO 接口手动释放 GIL,这个其实你看看 CPython 源代码就知道了。


Python的线程在遇到IO操作时,确实会自动释放GIL(全局解释器锁),让其他线程有机会执行。这个机制是由Python解释器内部实现的,具体来说:

  1. IO操作检测:当线程执行标准库中的IO操作(如文件读写、网络请求等)时,这些操作会调用底层的C函数。这些C函数在进入阻塞状态前,会主动释放GIL。

  2. GIL释放机制:在Python的C实现中,有一个宏Py_BEGIN_ALLOW_THREADS,它会在IO操作前释放GIL;对应的Py_END_ALLOW_THREADS会在操作完成后重新获取GIL。例如,socket.recv()这样的函数在底层C代码中就会使用这些宏。

  3. 自动切换:GIL释放后,操作系统线程调度器会决定哪个线程(包括其他Python线程或非Python线程)获得CPU时间片。其他Python线程此时可以获取GIL并执行。

这里有个简单的代码示例展示IO操作时GIL的释放:

import threading
import time
import requests

def io_task(url):
    print(f"线程 {threading.current_thread().name} 开始IO操作")
    response = requests.get(url)  # 这里会释放GIL
    print(f"线程 {threading.current_thread().name} 完成,状态码: {response.status_code}")

# 创建两个线程同时进行网络请求
threads = []
for i in range(2):
    t = threading.Thread(target=io_task, args=("https://httpbin.org/delay/1",))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

在这个例子中,两个线程几乎同时开始网络请求,因为第一个线程在requests.get()时释放了GIL,第二个线程就能立即执行。

需要注意的是,纯CPU密集型操作(比如大量数学计算)不会自动释放GIL,除非线程主动让出(比如调用time.sleep(0))。对于CPU密集型任务,建议使用多进程或异步编程。

总结:Python线程在IO操作时会通过底层C函数自动释放GIL。

最简单的, Python 标准库中所有的 io 操作都记录一下, 那么你基于标准实现的其他库你可以就知道了.


实在不行, 你还可以看看操作系统是不是有什么接口可以使用

do {
Py_BEGIN_ALLOW_THREADS
ret = fcntl(fd, code, buf);
Py_END_ALLOW_THREADS
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));

能否解释下,感觉只看懂了线程开始和结束,循环 fcntl 是否结束

Py_BEGIN_ALLOW_THREADS 这个宏的意思就是释放 GIL,然后 Py_END_ALLOW_THREADS 再锁上

IO 的核心部分其实是系统内核完成的,调用内核的时候放开就行了

线程执行是抢占式的 是由系统来调度的

回到顶部