手记

全局解释锁-GIL

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


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


一,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,现在很多默认环境就是CPython的Python,GIL的存在更多的是历史原因。

二,作用机制

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

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

三,python实现多任务

在Python中可以使用多线程,但不要指望能有效利用多核,多线程的并发在Python中就是一个dream。

若一定要通过多线程利用多核,那只能通过C扩展来实现(重写一个不带GIL的解释器),也可以通过多进程实现多核任务,这样还可以防止死锁发生。
单核用多线程的话,如果多任务一旦多到一个限度,会把大量时间用在切换上,就会消耗掉系统所有的资源,结果效率也是急剧下降的。我跟着代码跑了两次,计算密集情况下,发现单线程比多线程效率高了将近20%,

要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数(代码获取核心数multiprocessing.cpu_count( )  ),但是Python这样的脚本语言运行效率很低(这种情况当然C语言最NB)。
在计算任务多但是不大情况下,进程因为创建和调度进程带来的开销要远超出它的正面效应,多线程也需要缓存等等,这个时候就要用到单线程的异步编程模型,也是协程(python的天生优良基因),协程只是单纯的操作CPU的上下文,会大大提高效率。

原文出处

0人推荐
随时随地看视频
慕课网APP