关于Python中的GIL的限制和注意点
2021年1月6日 - 由Bo 0 评论 1704 阅读
谈起Python,大家都会说好用方便支持多,但是也同样会提到效率问题。
效率算是解释型问题的通病。虽然Python会编译出.pyc文件通过解释器执行字节码,但不少情况下仍然是解释逐条语句执行。而且大家平时使用的Python是CPython即由c编写的,那么效率上就不可能与c相提并论,而且CPython也不支持JIT(即时编译,目前只有pypy支持)。另外Python还有一个大锁叫做GIL,这把锁也限制了python利用多核cpu来运算。
GIL是全局解释器锁(Global Interpreter Lock),python用GIL来同步全局的线程,使得任何时刻只有一个线程在工作。GIL作为全局的线程的锁,解释器在执行python代码时,都需要获取这把锁,执行一会再释放给其他线程,这样交替。这样也导致了多线程不能利用多核cpu来运算。
为什么还会存在GIL呢,wiki python 给出了解释,主要是因为内存管理并不是线程安全的:
In CPython, the global interpreter lock, or GIL, is a mutex that protects access to Python objects, preventing multiple threads from executing Python bytecodes at once.
The GIL prevents race conditions and ensures thread safety.
In short, this mutex is necessary mainly because CPython's memory management is not thread-safe.
另外也指出了:A nice explanation of how the Python GIL helps in these areas can be found here.
因为无法利用多核处理器的优势,所以影响了计算密集型的程序。
如果需要避开GIL的限制:
1. 采用多进程而不是多线程。
比如使用multiprocessing。每个进程都分配了不同的解释器,分别带有不同的GIL。这样能利用多核处理器。不过对进程间通信造成了额外的开销。
2. 使用c语言扩展模块。
python跟c的关系是比较好的。就像numpy和scipy的核心部分都是c写的。
而python也有与c互通的模块,比如ctypes,可以让python调用c中的函数。ctypes在调用c代码时会释放GIL。但此时需要保证c的代码能与python分开独立运行,以避免造成阻塞而无法释放GIL的情况。
3. JPython和IronPython没有GIL。但是所用的库很可能并不兼容。
另外需要注意的是:
GIL并非完全没有意义存在,也不是一定要绕过。对于计算密集型的程序,我们可以用多进程;对于IO密集型的程序,多进程和多线程并没有多少区别,可以使用多线程,而且线程之间的通信也会简单一些。
另外计算密集型的可以考虑用c实现再用python调用,io密集型的再快也得等待硬盘。