Python多线程多核(python的多线程是否能利用多核计算)

1.python的多线程是否能利用多核计算

比方我有一个4核的CPU,那么这样一来,在单位时间内每个核只能跑一个线程,然后时间片轮转切换。

但是Python不一样,它不管你有几个核,单位时间多个核只能跑一个线程,然后时间片轮转。看起来很不可思议?但是这就是GIL搞的鬼。

任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。

通常我们用的解释器是官方实现的CPython,要真正利用多核,除非重写一个不带GIL的解释器。

2.Python如何利用多核处理器

GIL 与 Python 线程的纠葛 GIL 是什么东西?它对我们的 python 程序会产生什么样的影响?我们先来看一个问题。

运行下面这段 python 程序,CPU 占用率是多少?# 请勿在工作中模仿,危险:)def dead_loop(): while True: passdead_loop() 答案是什么呢,占用 100% CPU?那是单核!还得是没有超线程的古董 CPU。在我的双核 CPU 上,这个死循环只会吃掉我一个核的工作负荷,也就是只占用 50% CPU。

那如何能让它在双核机器上占用 100% 的 CPU 呢?答案很容易想到,用两个线程就行了,线程不正是并发分享 CPU 运算资源的吗。可惜答案虽然对了,但做起来可没那么简单。

下面的程序在主线程之外又起了一个死循环的线程 import threadingdef dead_loop(): while True: pass# 新起一个死循环线程t = threading.Thread(target=dead_loop)t.start()# 主线程也进入死循环dead_loop()t.join() 按道理它应该能做到占用两个核的 CPU 资源,可是实际运行情况却是没有什么改变,还是只占了 50% CPU 不到。这又是为什么呢?难道 python 线程不是操作系统的原生线程?打开 system monitor 一探究竟,这个占了 50% 的 python 进程确实是有两个线程在跑。

那这两个死循环的线程为何不能占满双核 CPU 资源呢?其实幕后的黑手就是 GIL。GIL 的迷思:痛并快乐着 GIL 的全称为 Global Interpreter Lock ,意即全局解释器锁。

在 Python 语言的主流实现 CPython 中,GIL 是一个货真价实的全局线程锁,在解释器解释执行任何 Python 代码时,都需要先获得这把锁才行,在遇到 I/O 操作时会释放这把锁。如果是纯计算的程序,没有 I/O 操作,解释器会每隔 100 次操作就释放这把锁,让别的线程有机会执行(这个次数可以通过sys.setcheckinterval 来调整)。

所以虽然 CPython 的线程库直接封装操作系统的原生线程,但 CPython 进程做为一个整体,同一时间只会有一个获得了 GIL 的线程在跑,其它的线程都处于等待状态等着 GIL 的释放。这也就解释了我们上面的实验结果:虽然有两个死循环的线程,而且有两个物理 CPU 内核,但因为 GIL 的限制,两个线程只是做着分时切换,总的 CPU 占用率还略低于 50%。

看起来 python 很不给力啊。GIL 直接导致 CPython 不能利用物理多核的性能加速运算。

那为什么会有这样的设计呢?我猜想应该还是历史遗留问题。多核 CPU 在 1990 年代还属于类科幻,Guido van Rossum 在创造 python 的时候,也想不到他的语言有一天会被用到很可能 1000+ 个核的 CPU 上面,一个全局锁搞定多线程安全在那个时代应该是最简单经济的设计了。

简单而又能满足需求,那就是合适的设计(对设计来说,应该只有合适与否,而没有好与不好)。怪只怪硬件的发展实在太快了,摩尔定律给软件业的红利这么快就要到头了。

短短 20 年不到,代码工人就不能指望仅仅靠升级 CPU 就能让老软件跑的更快了。在多核时代,编程的免费午餐没有了。

如果程序不能用并发挤干每个核的运算性能,那就意谓着会被淘汰。对软件如此,对语言也是一样。

那 Python 的对策呢?Python 的应对很简单,以不变应万变。在最新的 python 3 中依然有 GIL。

之所以不去掉,原因嘛,不外以下几点:欲练神功,挥刀自宫:CPython 的 GIL 本意是用来保护所有全局的解释器和环境状态变量的。如果去掉 GIL,就需要多个更细粒度的锁对解释器的众多全局状态进行保护。

或者采用 Lock-Free 算法。无论哪一种,要做到多线程安全都会比单使用 GIL 一个锁要难的多。

而且改动的对象还是有 20 年历史的 CPython 代码树,更不论有这么多第三方的扩展也在依赖 GIL。对 Python 社区来说,这不异于挥刀自宫,重新来过。

就算自宫,也未必成功:有位牛人曾经做了一个验证用的 CPython,将 GIL 去掉,加入了更多的细粒度锁。但是经过实际的测试,对单线程程序来说,这个版本有很大的性能下降,只有在利用的物理 CPU 超过一定数目后,才会比 GIL 版本的性能好。

这也难怪。单线程本来就不需要什么锁。

单就锁管理本身来说,锁 GIL 这个粗粒度的锁肯定比管理众多细粒度的锁要快的多。而现在绝大部分的 python 程序都是单线程的。

再者,从需求来说,使用 python 绝不是因为看中它的运算性能。就算能利用多核,它的性能也不可能和 C/C++ 比肩。

费了大力气把 GIL 拿掉,反而让大部分的程序都变慢了,这不是南辕北辙吗。难道 Python 这么优秀的语言真的仅仅因为改动困难和意义不大就放弃多核时代了吗?其实,不做改动最最重要的原因还在于:不用自宫,也一样能成功!其它神功 那除了切掉 GIL 外,果然还有方法让 Python 在多核时代活的滋润?让我们回到本文最初的那个问题:如何能让这个死循环的 Python 脚本在双核机器上占用 100% 的 CPU?其实最简单的答案应该是:运行两个 python 死循环的程序!也就是说,用两个分别占满一个 CPU 内核的 python 进程来做到。

确实,多进程也是利用多个 CPU 的好方法。只是进程间内存地址空间独立,互相协同通信要比多线程麻烦很多。

有感于此,Python 在 2.6 里新引入了 multiprocessing这个多进程标准库,让多进程的 python 程序编写简化到类。

3.python 多进程和多线程配合

由于python的多线程中存在PIL锁,因此python的多线程不能利用多核,那么,由于现在的计算机是多核的,就不能充分利用计算机的多核资源。

但是python中的多进程是可以跑在不同的cpu上的。因此,尝试了多进程+多线程的方式,来做一个任务。

比如:从中科大的镜像源中下载多个rpm包。#!/usr/bin/pythonimport reimport commandsimport timeimport multiprocessingimport threadingdef download_image(url):print '*****the %s rpm begin to download *******' % url commands.getoutput('wget %s' % url)def get_rpm_url_list(url):commands.getoutput('wget %s' % url) rpm_info_str = open('index.html').read() regu_mate = '(?<=)' rpm_list = re.findall(regu_mate, rpm_info_str) rpm_url_list = [url + rpm_name for rpm_name in rpm_list] print 'the count of rpm list is: ', len(rpm_url_list) return rpm_url_ def multi_thread(rpm_url_list):threads = [] # url = 'mands.getoutput('wget %s' %each_url) def main():url = '/centos/7/os/x86_64/Packages/' url_paas = '/centos/7.3.1611/paas/x86_64/openshift-origin/' url_paas2 ='/fedora/development/26/Server/x86_64/os/Packages/u/' start_time = time.time() rpm_list = get_rpm_url_list(url_paas) print multi_process(rpm_list) # print multi_thread(rpm_list)#print multi_process()# print multi_thread(rpm_list)# for index in range(len(rpm_list)):# print 'rpm_url is:', rpm_list[index] end_time = time.time() print 'the download time is:', end_time - start_timeprint main() 代码的功能主要是这样的: main()方法中调用get_rpm_url_list(base_url)方法,获取要下载的每个rpm包的具体的url地址。其中base_url即中科大基础的镜像源的地址,比如:/centos/7.3.1611/paas/x86_64/openshift-origin/,这个地址下有几十个rpm包,get_rpm_url_list方法将每个rpm包的url地址拼出来并返回。

multi_process(rpm_url_list)启动多进程方法,在该方法中,会调用多线程方法。该方法启动4个多进程,将上面方法得到的rpm包的url地址进行分组,分成4组,然后每一个组中的rpm包再最后由不同的线程去执行。

从而达到了多进程+多线程的配合使用。 代码还有需要改进的地方,比如多进程启动的进程个数和rpm包的url地址分组是硬编码,这个还需要改进,毕竟,不同的机器,适合同时启动的进程个数是不同的。

4.为什么有人说 Python 的多线程是鸡肋

Python多线程是不是鸡肋,是,GIL那个东西再那里摆着,就算在多核下面Python也是无法并行的,这个好理解嘛,就相当于做了个分时复用。

Python多线程有没有用,有,你去爬图片站的时候,用单进程单线程这种方式,进程很容易阻塞在获取数据socket函数上,多线程可以缓解这种情况。你说解决没有,要是每个请求都阻塞起了,那多线程也没什么用(当然,这种情况没见过哈)。

Python的优势就在于写起来快,用起来方便。你要做计算密集型的,还想并行化的话,还是用C吧。

Python多线程多核

转载请注明出处代码入门网 » Python多线程多核(python的多线程是否能利用多核计算)

资讯

numpypython版本(python怎么查看安装numpy的版本)

阅读(26)

本文主要为您介绍numpypython版本,内容包括python怎么查看安装numpy的版本,python3.4安装什么版本的numpy,python26,numpy安装包。命令行下查看python和numpy的版本和安装位置查看python版本方法一:python -V注意:‘-V中V为大写字母,只有一个

资讯

python3标准库(Python常用的标准库以及第三方库有哪些)

阅读(23)

本文主要为您介绍python3标准库,内容包括Python常用的标准库以及第三方库,Python常用的标准库以及第三方库,python标准库。标库Python拥有一个强大的标准库。Python语言的核心只包含数字、字符串、列表、字典、文件等常见类型和函数,而由Pyt

资讯

python调用另一个文件(python如何调用另一个py文件的所有函数)

阅读(24)

本文主要为您介绍python调用另一个文件,内容包括python如何在一个文件中调用另一个文件的类,python如何调用另一个py文件的所有函数,python怎么在一个文件中调用另一个文件的类。在同一个文件夹下 调用函数:A.py文件:B.py文件:或 调用类:A.py文

资讯

python按顺序(python顺序表)

阅读(24)

本文主要为您介绍python按顺序,内容包括python顺序表,求python字典中根据值的大小,按顺序排列键的方法,python从键盘输入a、b两个数,按大小顺序输出它们。本问题回答如下:你可以根据自己的需求稍微改动一下# -*- coding: cp936 -*-class Stu

资讯

python自动提交(如何用python脚本提交JS的表单)

阅读(23)

本文主要为您介绍python自动提交,内容包括如何用python脚本提交JS的表单,如何利用python进行参数的自动填写,怎么用python提交multipart/formdata数据。如果是要提交表单内容给 服务器的 python 处理,那么只需要在表单 <form&gt; 里面的 acti

资讯

python(abi)=2.4is(复数3+4i在Python中的表示方式是)

阅读(25)

本文主要为您介绍python(abi)=2.4is,内容包括linux下安装yum时报错需要python(abi)=2.4请问怎么处理,复数3+4i在Python中的表示方式是,Python问题给出下列代码:zhanyi=“用心战疫,共待花期”可以输出。在python中,复数的表示是【实数部+虚数

资讯

pythondate类型(python的日期类型转换)

阅读(15)

本文主要为您介绍pythondate类型,内容包括python的日期类型转换,python的日期类型转换,pythondatetime.datetime参数什么类型。你可以利用 time 模块里的 strptime()和 strftime()。strptime()根据你指定的格式控制字符串解读日期,而

资讯

python3的编码问题(怎么解决Python3乱码问题)

阅读(13)

本文主要为您介绍python3的编码问题,内容包括怎么解决Python3乱码问题,怎么解决Python3乱码问题,遇到python编码错误要怎么解决。python 3和2很大区别就是python本身改为默认用unicode编码。字符串不再区分"abc"和u"abc", 字符串"abc"

资讯

python创建文件路径(python如何创建文件夹)

阅读(15)

本文主要为您介绍python创建文件路径,内容包括python如何创建文件夹,请教各位如何用python创建文件和文件夹,怎么在Path中添加Python路径。主要涉及到三个函数os.path.exists(path) 判断一个目录是否存在2、os.makedirs(path) 多层创建目录

资讯

python开发的桌面软件(为什么用Python开发桌面应用程序)

阅读(11)

本文主要为您介绍python开发的桌面软件,内容包括python开发的桌面软件,python用来开发桌面软件可有什么不错的框架,python适合开发桌面软件吗。1)wxWidgets是一个比MFC优雅的库,TortoiseCVS用wxWidges而不用MFC,就是因为wxWidgets好用,而不

资讯

python入口参数(Python方法的几种常见参数类型)

阅读(13)

本文主要为您介绍python入口参数,内容包括Python方法的几种常见参数类型,python获取输入参数,命令行的python怎么传入参数。无默认值参数(关键字参数):def myfun(a): print(a)这是参数的最简单形式。这个a就是无默认值参数。在调用函数时必需

资讯

python排行榜(python列表从大到小排序)

阅读(14)

本文主要为您介绍python排行榜,内容包括python列表从大到小排序,python排名为什么升这么快,市场这么火,Python真的有那么厉害吗。您好,这个可以使用sorted()函数或sort()方法。比如我们定义:list1 = [3,34,9,233,92]那么你就可以使用:

资讯

numpypython版本(python怎么查看安装numpy的版本)

阅读(26)

本文主要为您介绍numpypython版本,内容包括python怎么查看安装numpy的版本,python3.4安装什么版本的numpy,python26,numpy安装包。命令行下查看python和numpy的版本和安装位置查看python版本方法一:python -V注意:‘-V中V为大写字母,只有一个

资讯

python3标准库(Python常用的标准库以及第三方库有哪些)

阅读(23)

本文主要为您介绍python3标准库,内容包括Python常用的标准库以及第三方库,Python常用的标准库以及第三方库,python标准库。标库Python拥有一个强大的标准库。Python语言的核心只包含数字、字符串、列表、字典、文件等常见类型和函数,而由Pyt

资讯

python调用另一个文件(python如何调用另一个py文件的所有函数)

阅读(24)

本文主要为您介绍python调用另一个文件,内容包括python如何在一个文件中调用另一个文件的类,python如何调用另一个py文件的所有函数,python怎么在一个文件中调用另一个文件的类。在同一个文件夹下 调用函数:A.py文件:B.py文件:或 调用类:A.py文

资讯

python按顺序(python顺序表)

阅读(24)

本文主要为您介绍python按顺序,内容包括python顺序表,求python字典中根据值的大小,按顺序排列键的方法,python从键盘输入a、b两个数,按大小顺序输出它们。本问题回答如下:你可以根据自己的需求稍微改动一下# -*- coding: cp936 -*-class Stu

资讯

python自动提交(如何用python脚本提交JS的表单)

阅读(23)

本文主要为您介绍python自动提交,内容包括如何用python脚本提交JS的表单,如何利用python进行参数的自动填写,怎么用python提交multipart/formdata数据。如果是要提交表单内容给 服务器的 python 处理,那么只需要在表单 <form&gt; 里面的 acti

资讯

python(abi)=2.4is(复数3+4i在Python中的表示方式是)

阅读(25)

本文主要为您介绍python(abi)=2.4is,内容包括linux下安装yum时报错需要python(abi)=2.4请问怎么处理,复数3+4i在Python中的表示方式是,Python问题给出下列代码:zhanyi=“用心战疫,共待花期”可以输出。在python中,复数的表示是【实数部+虚数

资讯

pythonwindows编码转换(python编码转换)

阅读(38)

本文主要为您介绍pythonwindows编码转换,内容包括python编码转换,python中前后台怎么进行编码转换,python编码转换。A. Usage decode/encode:def utf2gbk(s): 按utf-8解码,再按gbk编码 return s.dec

资讯

linuxpython库位置(怎样查看python安装路径)

阅读(1)

本文主要为您介绍linuxpython库位置,内容包括linuxpythonos库在什么地方,linuxpython类库在哪,centos中python的类库文件安装在什么位置。方法如下:linux查看python安装位置。有时我们需要查看python的安装位置,好安装第三方库,linux可以先pyt

资讯

python爬虫是什么(python爬虫是什么)

阅读(1)

本文主要为您介绍python爬虫是什么,内容包括python爬虫是什么,python为什么叫爬虫,python里面的爬虫是什么。世界上80%的爬虫是基于Python开发的,学好爬虫技能,可为后续的大数据分析、挖掘、机器学习等提供重要的数据源。什么是爬虫?网络爬虫(

资讯

python未知数(python的不等号是什么)

阅读(1)

本文主要为您介绍python未知数,内容包括请问python怎么用变量表示未知数之间的关系,请问python怎么用变量表示未知数之间的关系,python的不等号是什么。python的不等号如下:!= 不等于 - 比较两个对象是否不相等。

资讯

pythonxef(Python里的flag)

阅读(1)

本文主要为您介绍pythonxef,内容包括pythondef是什么意思,Python里的flag,python里defdrawGap是什么意思。

资讯

pythondll结构体(c结构体怎么用python写)

阅读(1)

本文主要为您介绍pythondll结构体,内容包括c结构体怎么用python写,python怎么构造一个结构体,python怎么导入ctypes。C封装了一个Dll,名为SpjMatlabTest.dll,暴露实现: typedef struct{ double *Min; dou

资讯

python小项目练手(推荐几个适合新手练手的Python项目)

阅读(1)

本文主要为您介绍python小项目练手,内容包括推荐几个适合新手练手的Python项目,python哪些练手的小程序,Python的练手项目值得推荐。实例一:满分推荐,非常值得练习GitHub - Yixiaohan/show-me-the-code: Python 练习册,每天一个小

资讯

python与c语言的区别是(python与C的区别)

阅读(1)

本文主要为您介绍python与c语言的区别是,内容包括python与C的区别,C语言和Python有什么区别呢,c语言和python区别。python与C的区别如下。语言类型不同。Python是一种动态类型语言,又是强类型语言。它们确定一个变量的类型是在您第一次给它

资讯

python3创建文件(python怎么创建一个txt文件)

阅读(1)

本文主要为您介绍python3创建文件,内容包括Python中如何创建文件,python怎么创建一个txt文件,python如何创建文件夹。python怎么创建一个txt文件的方法。如下参考:1.首先使用内置的空闲编辑器进行编辑(右键单击并选择copy),如下图所示。

资讯

python发布服务器(如何用Python搭建一个服务器)

阅读(1)

本文主要为您介绍python发布服务器,内容包括python怎么发布到服务器,如何用Python做大文件上传的服务端,怎么将python部署在服务器上。利用Python自带的包可以建立简单的web服务器。在DOS里cd到准备做服务器根目录的路径下,输入命令:python -