全局解释锁-GIL

试了好几种多任务模式Spider,从单进程到多线程,又试了多进程以及插入协程,纠结于效率问题又了解到GIL,在这里记个笔记,粗略总结一下不同场景下的多任务模式的选择


先说结论:python多线程鸡肋,多进程好比分布式,协程好比异步IO。

一,GIL及其由来
首先 解释性的编程语言都会有GIL,它是解释型语言的一种安全机制,因为解释型编程语言 会先执行代码,程序执行以后才做语法检查,不像编译型会在执行前检查再生成可执行文件(稳定)
GIL全称Global Interpreter Lock
官方文档
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.

可以看出:GIL并不是Python的特性,而是CPython解释器,Python完全可以不依赖于GIL,目前很多默认python环境就是CPython的,GIL的存在更多的是历史原因。

二,作用机制

Python的线程虽然是真正的线程,但解释器执行代码时,任何Python线程执行前,必须先获得GIL锁,然后,每执行100次操作(龟叔开发python的时候只有单核,为了让各个线程能够平均利用CPU时间,python会计算当前已执行的微代码数量,通过 sys.getcheckinterval() 查看),解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行。

再者,我们都知道线程是共享全局变量的,解决多线程之间数据完整性和状态同步的最简单方法自然也是加锁,那么多锁很容易造成死锁,导致多个线程全部挂起,既不能执行,也无法结束,只能靠操作系统强制终止。

三,python实现多任务

在Python中可以使用多线程,I/O密集情况下,GIL会在这个I/O调用之前被释放,以允许其他线程在这个线程等待I/O的时候运行,这个情况下的多线程还是比单线程有所提高的。但不要指望能有效利用多核,多线程的并发在Python中就是一个dream。

若一定要通过多线程利用多核,在计算密集型下,可以通过C扩展来实现(重写一个不带GIL的解释器不太现实),可以把一些 计算密集型任务用C语言编写,生成动态库,然后把.so链接库内容加载到Python中,因为执行C代码,GIL锁会释放,这样一来,就可以做到每个核都跑一个线程的目的;最普遍就是通过多进程实现多核任务(代码获取核心数multiprocessing.cpu_count( ) ),每个进程都有单独的内存空间,GIL只对同一空间内线程起作用,这样还可以防止死锁发生。

单核用多线程的话,如果多任务一旦多到一个限度,除了GIL,系统也会把大量时间用在切换上,效率也是急剧下降的。我跟着代码跑了两次,计算密集情况下,发现单线程比多线程效率高了将近20%,

多进程在计算任务多但是不大情况下,进程因为创建和调度进程带来的开销要远超出它的正面效应,多线程也需要缓存等等,这个时候就要用到单线程的异步编程模型,也是协程(python的天生优良基因),协程只是单纯的操作CPU的上下文,会大大提高效率。线程适用密集型IO(服务器),协程适用于任何IO。


更多请参考大牛CENALULU’S TECH BLOG

阅读更多
版权声明: https://blog.csdn.net/kiKimonster/article/details/79959195
文章标签: GIL
个人分类: other
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭