Python的多线程GIL浅谈
来源知乎:https://www.zhihu.com/question/23474039/answer/269526476
在介绍Python中的线程之前,先明确一个问题,Python中的多线程是假的多线程! 为什么这么说,我们先明确一个概念,全局解释器锁(GIL)。Python代码的执行由Python虚拟机(解释器)来控制。Python在设计之初就考虑要在主循环中,同时只有一个线程在执行,就像单CPU的系统中运行多个进程那样,内存中可以存放多个程序,但任意时刻,只有一个程序在CPU中运行。同样地,虽然Python解释器可以运行多个线程,只有一个线程在解释器中运行。对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同时只有一个线程在运行。在多线程环境中,Python虚拟机按照以下方式执行。1.设置GIL。2.切换到一个线程去执行。3.运行。4.把线程设置为睡眠状态。5.解锁GIL。6.再次重复以上步骤。对所有面向I/O的(会调用内建的操作系统C代码的)程序来说,GIL会在这个I/O调用之前被释放,以允许其他线程在这个线程等待I/O的时候运行。如果某线程并未使用很多I/O操作,它会在自己的时间片内一直占用处理器和GIL。也就是说,I/O密集型的Python程序比计算密集型的Python程序更能充分利用多线程的好处。我们都知道,比方我有一个4核的CPU,那么这样一来,在单位时间内每个核只能跑一个线程,然后时间片轮转切换。但是Python不一样,它不管你有几个核,单位时间多个核只能跑一个线程,然后时间片轮转。看起来很不可思议?但是这就是GIL搞的鬼。任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。通常我们用的解释器是官方实现的CPython,要真正利用多核,除非重写一个不带GIL的解释器。
我们不妨做个试验:
#coding=utf-8
from multiprocessing import Pool
from threading import Thread from multiprocessing import Process def loop():
while True:
pass if __name__ == '__main__': for i in range(3):
t = Thread(target=loop)
t.start() while True:
pass
我的电脑是4核,所以我开了4个线程,看一下CPU资源占有率:

我们发现CPU利用率并没有占满,大致相当于单核水平。
而如果我们变成进程呢?
我们改一下代码:
#coding=utf-8
from multiprocessing import Pool
from threading import Thread from multiprocessing import Process def loop():
while True:
pass if __name__ == '__main__': for i in range(3):
t = Process(target=loop)
t.start() while True:
pass

结果直接飙到了100%,说明进程是可以利用多核的!
为了验证这是Python中的GIL搞得鬼,我试着用Java写相同的代码,开启线程,我们观察一下:
package com.darrenchan.thread;
public class TestThread {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
}
}
}).start();
}
while(true){
}
}
}

由此可见,Java中的多线程是可以利用多核的,这是真正的多线程!而Python中的多线程只能利用单核,这是假的多线程!
难道就如此?我们没有办法在Python中利用多核?当然可以!刚才的多进程算是一种解决方案,还有一种就是调用C语言的链接库。对所有面向I/O的(会调用内建的操作系统C代码的)程序来说,GIL会在这个I/O调用之前被释放,以允许其他线程在这个线程等待I/O的时候运行。我们可以把一些 计算密集型任务用C语言编写,然后把.so链接库内容加载到Python中,因为执行C代码,GIL锁会释放,这样一来,就可以做到每个核都跑一个线程的目的!
可能有的小伙伴不太理解什么是计算密集型任务,什么是I/O密集型任务?
计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。
计算密集型任务由于主要消耗CPU资源,因此,代码运行效率至关重要。Python这样的脚本语言运行效率很低,完全不适合计算密集型任务。对于计算密集型任务,最好用C语言编写。
第二种任务的类型是IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。
IO密集型任务执行期间,99%的时间都花在IO上,花在CPU上的时间很少,因此,用运行速度极快的C语言替换用Python这样运行速度极低的脚本语言,完全无法提升运行效率。对于IO密集型任务,最合适的语言就是开发效率最高(代码量最少)的语言,脚本语言是首选,C语言最差。
综上,Python多线程相当于单核多线程,多线程有两个好处:CPU并行,IO并行,单核多线程相当于自断一臂。所以,在Python中,可以使用多线程,但不要指望能有效利用多核。如果一定要通过多线程利用多核,那只能通过C扩展来实现,不过这样就失去了Python简单易用的特点。不过,也不用过于担心,Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。
来源:知乎
Python的多线程GIL浅谈的更多相关文章
- python with上下文的浅谈
python中的with一般用于上下文管理,什么是上下文管理,其实平时我们经常会用到,比如连接数据库 查询数据,操作完成后关闭连接. 还比如打开文件写入数据等操作. 具体实例: class Myres ...
- 如何初学python?资深程序员浅谈,教你学会入门python
我认为python应该是现在市面上最简单,也是最值钱的一门编程语言,所以学习的人是越来越多,但是,如何初学python?这个问题困扰着很多初学python的人,今天,给大家简单聊聊这个话题. 我曾经也 ...
- python浅谈正则的常用方法
python浅谈正则的常用方法覆盖范围70%以上 上一次很多朋友写文字屏蔽说到要用正则表达,其实不是我不想用(我正则用得不是很多,看过我之前爬虫的都知道,我直接用BeautifulSoup的网页标签去 ...
- 浅谈五大Python Web框架
转载:http://feilong.me/2011/01/talk-about-Python-web-framework 说到Web Framework,Ruby的世界Rails一统江湖,而Pytho ...
- 浅谈Python时间模块
浅谈Python时间模块 今天简单总结了一下Python处理时间和日期方面的模块,主要就是datetime.time.calendar三个模块的使用.希望这篇文章对于学习Python的朋友们有所帮助 ...
- [转]浅谈Python web框架
说到web framework,Ruby的世界Rails一统江湖,而Python则是一个百花齐放的世界,各种micro-framework.framework不可胜数,不完全列表见:http://wi ...
- 浅谈Python在信息学竞赛中的运用及Python的基本用法
浅谈Python在信息学竞赛中的运用及Python的基本用法 前言 众所周知,Python是一种非常实用的语言.但是由于其运算时的低效和解释型编译,在信息学竞赛中并不用于完成算法程序.但正如LRJ在& ...
- Python 浅谈注释的重要性
最近参加了一个比赛,然后看到队友编程的代码,我觉得真的是难以下咽,几乎每个字符都要咨询他,用老师的话来说,这就是山炮编程员,所以此时的我意识到写一篇关于注释程序的重要性了,因此特地的写一篇文章帮助大家 ...
- Python 浅谈编程规范和软件开发目录规范的重要性
最近参加了一个比赛,然后看到队友编程的代码,我觉得真的是觉得注释和命名规范的重要性了,因为几乎每个字符都要咨询他,用老师的话来说,这就是命名不规范的后续反应.所以此时的我意识到写一篇关于注释程序的重要 ...
随机推荐
- C#委托与事件--后续补充
委托.事件补充 针对昨天文章 委托:让方法可以跟简单对象一样作为参数进行传递,也就是将方法作为参数进行封装. 方法:本质就是代码段 其实也好理解,目的就是为了封装,多态,既然简单对象如int i可以做 ...
- 论中国为什么造不出cpu和操作系统
为什么呢?不是中国人不聪明. 而是中国缺乏科学研究的氛围 中国错过了计算机理论研究的黄金时期 中国人对计算机的了解是一支半解 中国缺乏对应的产业基础 中国缺乏计算机基础研究的氛围 计算机所运用的各种科 ...
- RegexKitLite编译报错
报错如下: Undefined symbols for architecture i386: "_uregex_open", referenced from: _rkl_getC ...
- 内置函数--global() 和 local()
一 . globals :返回当前作用域内全局变量的字典. >>> globals() {'__spec__': None, '__package__': None, '__bu ...
- javascript模块化编程库require.js的用法
随着javascript的兴起,越来越多的公司开始将JS模块化,以增加开发的效率和减少重复编写代码的.更是为了能更加容易的维护日后的代码,因为现在的随着人们对交互效果的越来越强烈的需求,我们的JS代码 ...
- java容器类分析:Collection,List,ArrayList
1. Iterable 与 Iterator Iterable 是个接口,实现此接口使集合对象可以通过迭代器遍历自身元素. public interface Iterable<T> 修饰符 ...
- R语言︱文本挖掘之中文分词包——Rwordseg包(原理、功能、详解)
每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 笔者寄语:与前面的RsowballC分词不同的 ...
- Oracle SQL Developer中SQL语句格式化快捷键
Oracle SQL Developer中SQL语句格式化快捷键 格式化SQL语句:Ctrl+F7
- Struts2实现文件上传(一)
Struts2实现文件上传 文件上传成功后结果页面 result.jsp: <%@ page language="java" import="java.util.* ...
- Nethogs - 网络流量监控工具
命令iftop来检查带宽使用情况.netstat用来查看接口统计报告.还有其他的一些工具Bandwidthd.Speedometer.Nethogs.Darkstat.jnettop.ifstat.i ...