gil 是 cpython 中限制多线程并行执行的机制,绕过方法包括:一、多进程;二、调用释放 gil 的 c 扩展;三、切换至 pypy/jython/graalpython;四、异步 i/o;五、python 与 rust/c 混合编程。

Python 全局解释器锁(GIL)是 CPython 解释器中一道强制串行化执行的机制,它在多线程场景下限制同一时刻仅有一个线程执行 Python 字节码。这种设计虽保障了内存管理的安全性,却也成为 CPU 密集型并发任务的性能瓶颈。以下是绕过与利用 GIL 的多种工程实践路径:
一、使用多进程替代多线程
CPython 中每个进程拥有独立的解释器和内存空间,因此各自持有独立的 GIL,天然规避线程间 GIL 竞争。适用于 CPU 密集型任务,如数值计算、图像处理等。
1、导入 multiprocessing 模块,定义目标函数,该函数应为可序列化的纯函数。
2、创建 Process 实例,将目标函数与参数传入 target 和 args 参数中。
立即学习“Python免费学习笔记(深入)”;
3、调用 start() 方法启动子进程,使用 join() 等待其完成。
4、对结果进行收集时,可通过 Queue 或 Pipe 进行进程间通信,避免直接共享可变对象。
二、调用释放 GIL 的 C 扩展或外部库
CPython 提供了明确的 API(如 Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS),允许 C 扩展在执行耗时的 I/O 或计算操作时主动释放 GIL,使其他线程得以运行。NumPy、Pandas、cv2 等主流库内部已大量采用此模式。
1、使用 NumPy 数组进行向量化运算,所有底层 BLAS 调用均自动释放 GIL。
2、调用 requests 库发起 HTTP 请求时,底层 socket 阻塞操作会自动释放 GIL,此时其他线程可继续执行。
3、在 Cython 中对计算密集型函数添加 nogil=True 声明,并确保函数内不访问 Python 对象。
三、切换至无 GIL 的 Python 实现
某些 Python 解释器不实现 GIL,因而原生支持真正的多线程并行。它们兼容大部分 Python 语法,但需注意 C 扩展兼容性与生态支持程度。
1、安装并使用 PyPy,其 JIT 编译器在多数场景下显著提升单线程性能,且对 threading 模块的支持已逐步优化。
2、在支持的系统上部署 Jython,运行于 JVM,完全无 GIL 并可直接调用 Java 多线程类库。
3、评估 GraalPython,在 GraalVM 上运行,支持多语言互操作与原生线程并发,需确认目标第三方包是否已通过 polyglot 接口适配。
四、异步 I/O 模式规避 GIL 阻塞
asyncio 本身不绕过 GIL,但通过事件循环调度协程,在单线程内高效复用 I/O 等待时间,避免线程因阻塞而空转。适用于高并发 I/O 场景,如 Web 服务、实时消息处理。
1、将阻塞式调用(如 time.sleep、requests.get)替换为 async 版本(如 asyncio.sleep、aiohttp.ClientSession)。
2、定义协程函数并使用 async def 声明,调用时通过 await 暂停执行并交还控制权。
3、使用 asyncio.run() 启动主协程,或在生产环境使用 uvloop 替换默认事件循环以提升吞吐。
五、混合编程:Python 与 Rust/C 协同
将 CPU 密集逻辑下沉至无 GIL 约束的系统级语言模块,Python 层仅负责调度与胶水逻辑。Rust 因其零成本抽象与线程安全模型,成为当前主流选择。
1、使用 PyO3 将 Rust 函数编译为 Python 可导入的 .so/.dll 扩展模块。
2、在 Rust 函数内使用 std::thread::spawn 启动原生线程,所有线程均不受 Python GIL 约束。
3、通过 PyO3 的 Python::allow_threads 方法,在 Rust 中显式进入/退出 GIL,确保回调 Python 对象时的线程安全。










