Python辣鸡,Python多线程不能并行?
文章目录
- 1. Python多线程的缺陷
- 1.1 Java单线程和多线程执行倒计时函数
- 1.2 Python单线程和多线程执行倒计时函数
- 2. GIL
- 2.1 什么是GIL
- 2.2 Python为什么不舍弃GIL
- 3. Python的多线程这么辣鸡,那还用不用?
- 3.1 多线程
- 3.2 多进程
- 3.3 协程
- 3.4 多进程 VS 多线程 VS 协程
1. Python多线程的缺陷
比如我现在要执行2个倒计时函数,
我可以选择用一个线程顺序执行这2个函数,按顺序倒计时2次;
也可以用两个线程并行这2个函数,也就是同时倒计时。
我的电脑有4个CPU核心,是可以并行4个线程的,因此我并行上述2个线程完全没问题。
也就是说,我用2个线程执行2个倒计时用的时间应该是一个线程执行2次倒计时时间的一半。
1.1 Java单线程和多线程执行倒计时函数
在Java中确实是如此,如下。
Java单进程执行:
如下代码,有一个倒计时函数countDown()
,我在主函数里顺序执行两次倒计时500000000下:
public class Main {private static void countDown(long count) {while (count > 0) {count--;}System.out.println("倒计时完成");}public static void main(String[] args) throws InterruptedException {long startTime = System.currentTimeMillis();countDown(500000000);countDown(500000000);long endTime = System.currentTimeMillis();System.out.println("耗时间为:" + (endTime - startTime));}
}
输出:
倒计时完成
倒计时完成
耗时间为:456
Java多线程执行:
public class Main {private static void countDown(long count) {while (count > 0) {count--;}System.out.println("倒计时完成");}public static void main(String[] args) throws InterruptedException {long startTime = System.currentTimeMillis();Thread t1 = new Thread(() -> countDown(500000000));Thread t2 = new Thread(() -> countDown(500000000));t1.start();t2.start();t1.join();t2.join();long endTime = System.currentTimeMillis();System.out.println("耗时间为:" + (endTime - startTime));}
}
输出:
倒计时完成
倒计时完成
耗时间为:274
使用了多线程确实速度变快了很多,花费时间几乎是单线程的一半。(为什么不是严格的一半?因为用了多线程之后,线程之间切换也需要开销)
但是Python的表现却不是这样
1.2 Python单线程和多线程执行倒计时函数
同样的,使用Python来对比单进程和多线程执行倒计时函数,如下:
Python单线程执行:
import time
from threading import Thread
# 倒计时函数
def count_down(count):while count > 0:count -= 1print("倒计时完成")start = time.time()count_down(50000000)
count_down(50000000)end = time.time()
print(f"耗时为:{end - start}")
输出:
倒计时完成
倒计时完成
耗时为:3.884136199951172
Python多线程执行:
import time
from threading import Thread
# 倒计时函数
def count_down(count):while count > 0:count -= 1print("倒计时完成")start = time.time()t1 = Thread(target=count_down, args=(50000000, ))
t2 = Thread(target=count_down, args=(50000000,))
t1.start()
t2.start()
t1.join()
t2.join()end = time.time()
print(f"耗时为:{end - start}")
输出:
倒计时完成
倒计时完成
耗时为:4.136513948440552
此时却发现,Python多线程执行用的时间比单线程用的时间还要多??
这是因为Python的多线程并不能并行!
为什么Python的多线程不能并行?
2. GIL
Python在同一时间,只有一个线程可以运行。但是这是为什么,就是Pyhton中的GIL进行了限制!
当线程要想执行,就要获取GIL,但是一个Python进程只有一个GIL,因此同一时刻只有一个线程可以执行。
那么GIL是什么?
2.1 什么是GIL
GIL全称叫做Global Interpreter Lock,翻译过来就是全局解释器锁,那么GIL有什么作用?
GIL的作用是用于Python的内存管理。
Python使用的自动内存管理机制,也就是创建的对象不用了之后就可以自动回收。
引用计数法:
Python使用引用记数法来给每个对象进行标记,每个对象有一个引用计数值count。
如果有变量引用该对象,就会给count +1;
如果一个变量不引用该对象了,就将count -1;
当一个对象的引用计数值count为0,该对象就可以回收了。
如下代码:
import sys
a = [] # a引用了该列表对象, 引用计数为1
b = a # b也引用了该列表对象,引用计数为2
print(sys.getrefcount(a)) # 这里该对象又被该方法的形参引用,引用计数为3
输出:
3
加锁:
也就是当一个变量引用一个对象,需要修改该对象的引用计数值,那么如果是多个进程同时引用某个对象,会同时修改该对象的引用计数值。,有可能就会造成某个对象的引用计数值count不正确,比如:
刚开始某个变量的计数值count是1,现在有两个线程同时引用该对象,也就是同时执行count += 1
操作。
而count += 1
其实是分为三步的:
1. 读取count的值
2. 修改count的值
有可能发生这种情况:
线程1读取count的值,count = 1
线程2读取count的值,count = 1
线程1修改count = 2
线程2修改count = 2
也就是count += 1
执行2次,结果应该是3,但是由于多线程同时执行,count修改后最终结果却是2。
而当count == 0时,该对象就会被回收,而上述的多线程同时修改count值的问题,会造成该对象不该回收却被回收了。
为了防止线程安全问题,就需要给每个对象的引用计数值加锁,那么每个对象的引用计数值都需要加锁,不但很麻烦,而且容易造成死锁。
因此Python在设计出来的时候,只设计了一把锁 : GIL,当一个线程需要执行,需要线获取GIL,然后执行,这样就不会有多个线程同时对一个对象的引用计数count进行操作,这样就很安全。
GIL只有1个,线程要获取GIL再执行,因此Python同一时刻只有一个线程可以执行。
既然由于GIL的限制,让Python多线程无法并行,为什么不去掉GIL,或者更改内存管理机制?
注: Python有低层有多种实现,官方的是使用C实现的叫CPython,还是使用Java实现的叫Jython,C#实现的等等,但是只有CPython受到GIL的限制。
2.2 Python为什么不舍弃GIL
Python成也GIL,败也GIL。
GIL导致多线程无法并行,因此是被最多人诟病的一点,但为什么至今CPython还在使用呢?
Python的第三方库非常丰富,使用C写的,由于GIL让同一个时刻只有一个线程执行,保证了线程安全,因此这些库在设计的时候不用考虑线程安全问题,实现起来非常容易。
如果现在修改的话,那么那么库都不能使用了。
并且很多人认为,程序执行效率影响最大的不是CPU而是I/O。
GIL结构简单,效率非常高,如果使用其他的机制,效率会降低,因此GIL至今仍然在使用。
3. Python的多线程这么辣鸡,那还用不用?
在前面那个倒计时的例子中,其实是一个极端的例子。一般程序可以分为三种:
- CPU密集型
- I/O密集型
- CPU和I/O均衡型
上面那个倒计时的例子,是属于CPU密集型,因为全部都是CPU指令。
但是由于GIL,同一时间只有一个线程可以执行,也只能利用到一个CPU的核心。因此对于CPU密集型,使用多线程不是最好的选择。
3.1 多线程
对于多进程来说,创建出多个进程,只不过增加了该程序被执行的概率。
因为操作系统的基本调度单位是线程,一个进程创建出多个线程,多个线程去争抢时间片,因此该进程执行的概率更大(就好比使用多个号参与抽奖,自己中奖的概率更高)。
但是Python的多线程由于GIL又不能利用到多个CPU核心。
3.2 多进程
GIL只是锁住了一个进程,如果使用多进程,就可以解除GIL的限制。
使用多进程的话,不同进程切换开销更大,但是起码多个进程可以并行,可以充分利用CPU的多个核心。
因此,对于CPU密集型,可以使用多进程。
还是倒计时的例子,使用多进程代码如下:
import time
from multiprocessing import Pool# 倒计时函数
def count_down(count):while count > 0:count -= 1print("倒计时完成")start = time.time()pool = Pool(processes=2)
p1 = pool.apply_async(count_down, [50000000])
p2 = pool.apply_async(count_down, [50000000])
pool.close()
pool.join()end = time.time()
print(f"耗时为:{end - start}")
输出:
倒计时完成
倒计时完成
耗时为:2.0172834396362305
可以看到,使用多进程可以实行两个进程并行执行倒计时函数,速度提高了不少。
3.3 协程
对于I/O密集型,多进程和多线程都不是也不一定是最好的选择。
多进程开销大可以充分利用CPU,但是I/O密集型程序执行CPU指令的时间比较少,用不到。
而多线程,需要操作系统参与调度,开销虽然比多进程小,但是还是有点大。
而Python原生实现了协程。
协程比线程更加轻量级,一个线程里面可以创建多个协程,并且协程调度有Python虚拟机来实现,不需要操作系统来参与。
对于I/O密集型程序,大部分时间都在等待I/O,CPU执行指令的时间比较少,比如爬虫,因此使用协程来实现比较合适。
3.4 多进程 VS 多线程 VS 协程
多进程 | 多线程 | 协程 | |
---|---|---|---|
是否并行 | 并行 | 非并行 | 非并行 |
调度开销 | 很大 | 大 | 小 |
适合程序类型 | CPU密集型 | 其他 | I/O密集型 |
Python辣鸡,Python多线程不能并行?相关推荐
- 辣鸡python导入不了函数库嘤嘤嘤(问题)
python萌新,用的cmd里pip install函数一直不行,pip已经是最新,python重装也不行. 一个萌新失去了梦想,求助
- ios拒审4.3 python自动生成辣鸡代码
配置文件config.json [{"key" : "jiebabuyuxiniubuyu","add_func_num" :1," ...
- python面试题之多线程好吗?列举一些让Python代码以并行方式运行的方法
答案 Python并不支持真正意义上的多线程.Python中提供了多线程包,但是如果你想通过多线程提高代码的速度,使用多线程包并不是个好主意.Python中有一个被称为Global Interpret ...
- Python 多进程开发与多线程开发
我们先来了解什么是进程? 程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本:进程 ...
- python中的多任务-多线程和多进程
多线程和多进程都是实现多任务的一种方式,但是对于很多初学者来说想分清楚他们往往是一件非常头疼的事,首先我们需要了解多任务的概念. 所谓的多任务就是在同一时刻同时做很多事情,比如我们一边使用浏览器上网一 ...
- 从零开始学 Python 之 初识 Python 多线程
我们知道,多线程与单线程相比,可以提高 CPU 利用率,加快程序的响应速度. 单线程是按顺序执行的,比如用单线程执行如下操作: 6秒读取文件1 9秒处理文件1 5秒读取文件2 8秒处理文件2 总共用时 ...
- 一行 Python 实现并行化 -- 日常多线程操作的新思路 - 左手键盘,右手书 - SegmentFault...
一行 Python 实现并行化 -- 日常多线程操作的新思路 - 左手键盘,右手书 - SegmentFault
- python解决鸡兔同笼问题
python解决鸡兔同笼问题 参考文章: (1)python解决鸡兔同笼问题 (2)https://www.cnblogs.com/xiaolu915/p/10587499.html 备忘一下.
- Python学习笔记:多线程和多进程(转1)
前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...
最新文章
- DL之CNN:计算机视觉之卷积神经网络算法的简介(经典架构/论文)、CNN优化技术、调参学习实践、CNN经典结构及其演化、案例应用之详细攻略
- 图像二值化之最大类间方差法(大津法,OTSU)
- 关于父窗口获取跨域iframe子窗口中的元素
- boost::geometry::index::detail::segment_intersection用法的测试程序
- 软件设计过程经验谈 之 如何做好领域模型设计
- 统计app用户在线时长_「云工作普及系列」2.如何实时统计工作时长,提高工作效率
- mysql删除表单挑数据_MySQL 删除数据表
- python输入名字配对情侣网名_输入姓名配对qq网名,QQ情侣昵称
- Cover Protocol发起新提案,为Nexus Mutual提供保险覆盖
- shell脚本七十问
- E: Could not get lock /var/lib/dpkg/lock - open (11: Resource temporarily unavailable)
- 机器学习基础 -- 李宏毅2020机器学习课程笔记(一)
- SharePoint 2013 母版页修改后,无法添加应用程序
- 关于KL距离(KL Divergence)
- my-mind在线思维导图软件
- redis简略版笔记
- linux获取android界面,Android中 adb shell ps 查看手机中进程信息
- RK3568平台开发系列讲解(USB篇)libusb流程简介
- 三国志战略版:Daniel_平民福音-“黑科技阵法”三势阵
- 天天特惠系统秒杀优化方案