Python Web学习笔记之GIL机制下的鸡肋多线程
为什么有人会说 Python 多线程是鸡肋?知乎上有人提出这样一个问题,在我们常识中,多进程、多线程都是通过并发的方式充分利用硬件资源提高程序的运行效率,怎么在 Python 中反而成了鸡肋?
有同学可能知道答案,因为 Python 中臭名昭著的 GIL。
那么 GIL 是什么?为什么会有 GIL?多线程真的是鸡肋吗? GIL 可以去掉吗?带着这些问题,我们一起往下看,同时需要你有一点点耐心。
多线程是不是鸡肋,我们先做个实验,实验非常简单,就是将数字 “1亿” 递减,减到 0 程序就终止,这个任务如果我们使用单线程来执行,完成时间会是多少?使用多线程又会是多少?show me the code。
def decrement(n):
while n > 0:
n -= 1
单线程
import time start = time.time()
decrement(100000000)
cost = time.time() - start
>>> 6.541690826416016
在我的4核 CPU 计算机中,单线程所花的时间是 6.5 秒。可能有人会问,线程在哪里?其实任何程序运行时,默认都会有一个主线程在执行。
多线程
import threading start = time.time() t1 = threading.Thread(target=decrement, args=[50000000])
t2 = threading.Thread(target=decrement, args=[50000000]) t1.start() # 启动线程,执行任务
t2.start() # 同上 t1.join() # 主线程阻塞,直到t1执行完成,主线程继续往后执行
t2.join() # 同上 cost = time.time() - start >>>6.85541033744812
创建两个子线程 t1、t2,每个线程各执行 5 千万次减操作,等两个线程都执行完后,主线程终止程序运行。结果,两个线程以合作的方式执行是 6.8 秒,反而变慢了。按理来说,两个线程同时并行地运行在两个 CPU 之上,时间应该减半才对,现在不减反增。
是什么原因导致多线程不快反慢的呢?
原因就在于 GIL ,在 Cpython 解释器(Python语言的主流解释器)中,有一把全局解释锁(Global Interpreter Lock),在解释器解释执行 Python 代码时,先要得到这把锁,意味着,任何时候只可能有一个线程在执行代码,其它线程要想获得 CPU 执行代码指令,就必须先获得这把锁,如果锁被其它线程占用了,那么该线程就只能等待,直到占有该锁的线程释放锁才有执行代码指令的可能。
因此,这也就是为什么两个线程一起执行反而更加慢的原因,因为同一时刻,只有一个线程在运行,其它线程只能等待,即使是多核CPU,也没办法让多个线程「并行」地同时执行代码,只能是交替执行,因为多线程涉及到上线文切换、锁机制处理(获取锁,释放锁等),所以,多线程执行不快反慢。
什么时候 GIL 被释放呢?
当一个线程遇到 I/O 任务时,将释放GIL。计算密集型(CPU-bound)线程执行 100 次解释器的计步(ticks)时(计步可粗略看作 Python 虚拟机的指令),也会释放 GIL。可以通过 sys.setcheckinterval()设置计步长度,sys.getcheckinterval() 查看计步长度。相比单线程,这些多是多线程带来的额外开销。
CPython 解释器为什么要这样设计?
多线程是为了适应现代计算机硬件高速发展充分利用多核处理器的产物,通过多线程使得 CPU 资源可以被高效利用起来,Python 诞生于1991年,那时候硬件配置远没有今天这样豪华,现在一台普通服务器32核64G内存都不是什么司空见惯的事。
但是多线程有个问题,怎么解决共享数据的同步、一致性问题,因为,对于多个线程访问共享数据时,可能有两个线程同时修改一个数据情况,如果没有合适的机制保证数据的一致性,那么程序最终导致异常,所以,Python之父就搞了个全局的线程锁,不管你数据有没有同步问题,反正一刀切,上个全局锁,保证数据安全。这也就是多线程鸡肋的原因,因为它没有细粒度的控制数据的安全,而是用一种简单粗暴的方式来解决。
这种解决办法放在90年代,其实是没什么问题的,毕竟,那时候的硬件配置还很简陋,单核 CPU 还是主流,多线程的应用场景也不多,大部分时候还是以单线程的方式运行,单线程不要涉及线程的上下文切换,效率反而比多线程更高(在多核环境下,不适用此规则)。所以,采用 GIL 的方式来保证数据的一致性和安全,未必不可取,至少在当时是一种成本很低的实现方式。
那么把 GIL 去掉可行吗?
还真有人这么干多,但是结果令人失望,在1999年Greg Stein 和Mark Hammond 两位哥们就创建了一个去掉 GIL 的 Python 分支,在所有可变数据结构上把 GIL 替换为更为细粒度的锁。然而,做过了基准测试之后,去掉GIL的 Python 在单线程条件下执行效率将近慢了2倍。
Python之父表示:基于以上的考虑,去掉GIL没有太大的价值而不必花太多精力。
小结
CPython解释器提供 GIL 保证线程数据同步,那么有了 GIL,虽然无法达到高并发利用多核的优势,我们还需要线程同步做一些其他的事情。
Python Web学习笔记之GIL机制下的鸡肋多线程的更多相关文章
- Python Web学习笔记之为什么设计GIL
GIL(global interpreter lock),全局解释器锁,是很多编程语言实现中都具有的特性,由于它的存在,解释器无法实现真正的并发.它也是 Python 中经常讨论的话题之一. Pyth ...
- Python Web学习笔记之多线程编程
本次给大家介绍Python的多线程编程,标题如下: Python多线程简介 Python多线程之threading模块 Python多线程之Lock线程锁 Python多线程之Python的GIL锁 ...
- Python Web学习笔记之TCP/IP、Http、Socket的区别
经常在笔试.面试或者工作的时候听到这些协议,虽然以前没怎么涉及过,但至少知道这些是和网络编程密不可分的知识,作为一个客户端开发程序员,如果可以懂得网络编程的话,他的作用和能力肯定会提升一个档次.原因很 ...
- Python Web学习笔记之WebSocket原理说明
众所周知,Web应用的通信过程通常是客户端通过浏览器发出一个请求,服务器端接收请求后进行处理并返回结果给客户端,客户端浏览器将信息呈现.这种机制对于信息变化不是特别频繁的应用可以良好支撑,但对于实时要 ...
- Python Web学习笔记之Python多线程和多进程、协程入门
进程和线程究竟是什么?如何使用进程和线程?什么场景下需要使用进程和线程?协程又是什么?协程和线程的关系和区别有哪些? 程序切换-CPU时间的分配 首先,我们的任何一个程序都需要运行在一个操作系统中,如 ...
- Python Web学习笔记之TCP/IP协议原理与介绍
HTTP.FTP.SMTP.Telnet等等协议,哦!那个HTTP协议啊就是访问网页用的那个协议啊然后那个······其实······你懂得,我们应该从实际来了解他,理解网络协议的作用与功能,然后再从 ...
- Python Web学习笔记之并发编程的孤儿进程与僵尸进程
1.前言 之前在看<unix环境高级编程>第八章进程时候,提到孤儿进程和僵尸进程,一直对这两个概念比较模糊.今天被人问到什么是孤儿进程和僵尸进程,会带来什么问题,怎么解决,我只停留在概念上 ...
- Python Web学习笔记之Cookie,Session,Token区别
一.Cookie,Session,Token简介 # 这三者都解决了HTTP协议无状态的问题 session ID or session token is a piece of data that i ...
- Python Web学习笔记之图解TCP/IP协议和浅析算法
本文通过两个图来梳理TCP-IP协议相关知识.TCP通信过程包括三个步骤:建立TCP连接通道,传输数据,断开TCP连接通道.如图1所示,给出了TCP通信过程的示意图. 图1主要包括三部分:建立连接.传 ...
随机推荐
- xtrabackup安装部署(二)
在官网中,复制相关链接下载最新版本(建议使用当前发布版本前6个月左右的稳定版本) https://www.percona.com/downloads/XtraBackup/LATEST/ 1.下载和安 ...
- Keepalived+Nginx高可用集群
Keepalived简介 Keepalived的作用是检测服务器的状态,如果有一台web服务器宕机,或工作出现故障,Keepalived将检测到,并将有故障的服务器从系统中剔除,同时使用其他服务器代替 ...
- ubuntu16.04下安装ros-kinetic
参考:http://wiki.ros.org/kinetic/Installation/Ubuntu 1.添加ROS软件源 ~$ sudo sh -c 'echo "deb http://p ...
- codeforces 798B - Mike and strings
感觉自己好咸鱼呀……B题写了这么久,虽然可以算作1A(忽略一次少include一个头文件的CE)…… 思想很简单,每次选定一个字符串作为目标字符串,然后把其他所有字符串都当做测试字符串,计算出总共需要 ...
- CCCC L2-013. 红色警报 连通分量
题解:将问题转化成连通分量.每次失去一座城市,切断其所有的边,算一次现在的连通分量.若增量大于1,则发出警报. 至于如何算连通分量,直接用tarjan模板 坑://我昨天晚上半夜敲的模板,把一个算所有 ...
- Windows Preinstallation Environment
https://en.wikipedia.org/wiki/Windows_Preinstallation_Environment https://zh.wikipedia.org/wiki/Wind ...
- select.select的使用注意事项
1.select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制. 2.select()所维护的存储 ...
- Linux下Miniconda量化环境安装
前言 linux目录相关知识 /usr:系统级的目录,可以理解为C:/Windows/,/usr/lib理解为C:/Windows/System32./usr/local:用户级的程序目录,可以理解为 ...
- CSS表单设计
今天我们开始学习<十天学会web标准(div+css)>的css表单设计,包含以下内容和知识点: 改变文本框和文本域样式 用图片美化按钮 改变下拉列表样式 用label标签提升用户体验 一 ...
- table切换(自己写)
<!DOCTYPE HTML><html> <head> <meta charset="utf-8"> <meta name= ...