Python 里那个叫 GIL(Global Interpreter Lock)的玩意儿,说实话,要是你只是在写普通的小脚本,平时跑点爬虫、写写 Flask 之类的玩意儿,可能根本没感觉它的存在。但是一旦你开始认真搞多线程,尤其是那种想要并行加速的场景,GIL 就像个隐形的“中年油腻领导”一样,挡在你面前,说:“哥们儿,慢点儿,别都抢着干活。”
我刚接触 Python 多线程那会儿,特别天真,想着照搬 Java 那一套,开几个线程跑任务,总能快点吧?结果跑起来发现,这性能也没提升多少啊,甚至有时候还不如单线程。一开始我还以为是我代码写得烂(当然这也可能是真的),但查资料一看,好家伙,是 GIL 在作怪。那 GIL 到底是个啥?用一句话说就是:Python 解释器一次只能让一个线程执行字节码。注意哦,是“线程”不是“进程”。虽然你可以用 threading.Thread 开一堆线程,但这些线程不会真正并行执行 Python 的代码。它们会一个一个“轮流上厕所”,而不是大家一起冲进去洗手间“拼速度”。
GIL 是为了什么?主要是因为 CPython(就是咱们用得最多的 Python 实现)里,很多底层的数据结构,比如对象的引用计数,根本不是线程安全的。GIL 就像一个“安全带”,强制所有线程排队,避免数据混乱和程序崩溃。听起来还挺负责任的对吧?但问题是,这玩意儿一刀切地限制了多线程的并发能力。
说白了,Python 多线程在处理那种计算密集型任务的时候,比如你写个矩阵运算、图像处理啥的,压根没法利用多核 CPU 的优势,效率堪比拔牙。而如果你处理的是 IO 密集型任务,比如文件读写、网络请求,那情况还行,线程在等 IO 的时候会把 GIL 让出来,别的线程可以接着干活。
我举个真实场景吧。之前在一个项目里搞个图片批处理工具,要对几万张图进行压缩和滤镜处理,刚开始想当然地用了多线程,想着每个线程处理一张图,结果 CPU 使用率一直在50%左右飘,性能提升很有限。后来一怒之下换成了 multiprocessing 模块,每个子进程一个 Python 实例,直接无视 GIL,结果性能嗖嗖地飙升,CPU 基本吃满了。
所以讲真,如果你在 Python 里真的要搞并行计算,那就别指望 threading,直接上 multiprocessing 或者用 Cython/Numba 这种绕过 GIL 的方式,才是真正的正道。当然也有人建议用非 CPython 的解释器,比如 Jython、IronPython 或 PyPy,但现实中这几个能跑的库太少了,坑太多,不推荐轻易尝试。
再说个我后来发现的“坑中之坑”。有一次我试图用线程池(concurrent.futures.ThreadPoolExecutor)来异步处理数据库查询,想着数据库是 IO 操作嘛,多线程应该挺合适。结果发现线程池用着用着就卡住不动了。调了半天才发现,MySQL 的 Python 驱动并不是纯 IO 操作,它会在数据解码那一段吃掉 CPU,而且是在 GIL 下运行的。就是说,它让你以为是 IO 密集型,其实是个披着 IO 外衣的计算密集型任务。坑爹不?
所以用多线程之前,先扪心自问一个问题:你这代码到底是 CPU 密集型还是 IO 密集型?如果是前者,GIL 基本上是你无法跨越的鸿沟;后者倒是可以搞一搞线程。
哦对了,还有一个小知识点,GIL 本身并不是 Python 语言的特性,而是 CPython 的实现细节。理论上你可以实现一个没有 GIL 的 Python 解释器,只不过这活儿太硬核,目前主流还都离不开这根“锁命绳”。
当然啦,也有不少人喊着让 Python 摆脱 GIL,比如 Python 官方的“nogil”提案已经提出来好几年了,号称可以无痛去掉 GIL,提升并发能力。听起来很美,但说实话,我个人是悲观的。因为一旦 GIL 真去了,那 CPython 的一大堆库和扩展可能都得重写。风险太高,改动太大,保守派程序员估计是不会轻易买账的。
写到这儿,其实你也看出来了,GIL 是个历史包袱,也是一种技术妥协。它在某些方面确实保护了程序的稳定性,但同时也让 Python 成了并发编程领域的“弱鸡”。所以别再被那些“Python 多线程很强”之类的说法骗了,真正懂的人都知道,在 Python 里,线程更多是用来处理 IO 的。
那是不是说 Python 就不能搞高并发?也不是。用协程(比如 asyncio)搞 IO 并发,现在已经是主流姿势了,配合 uvloop 能跑得飞起。想要真并行就多进程,甚至可以考虑用 C 扩展或者借助 Rust、Go 搞成服务然后 Python 调用。总之方法不是没有,但要选对工具,别硬来。
最后总结一句——GIL 不是你代码烂的借口,但你不懂它,真的会被它坑得找不着北。兄弟们下次遇到 Python 多线程性能上不去的时候,别急着优化代码,先看看是不是 GIL 在后面“背刺”你了。
以上就是“解释一下 Python 中的解释器锁(Global Interpreter Lock,GIL),以及它对多线程编程的影响!”的详细内容,想要了解更多Python教程欢迎持续关注编程学习网。
扫码二维码 获取免费视频学习资料
- 本文固定链接: http://www.phpxs.com/post/13179/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料
查 看2022高级编程视频教程免费获取