本文主要综合三篇文章的内容,分为三个方面理解进程与线程。
前言
python可以实现多线程的方法有
- thread(低版本使用的),threading
- Queue
- multiprocessing
其中threading客户以通过实例方法或者类方法来创建 多线程。
multiprocessing方法可以创建多线程池,用起来比较简洁、方便。
两者的比较
multiprocessing实例化的pool:
Pool可以提供指定数量的线程供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的线程用来执行该请求;但如果池中的线程数已经达到规定最大值,那么该请求就会等待,直到池中有线程结束,才会创建新的线程来它。
threading实例化多线程类:
没有仔细研究过有点,目前姑且认为 multiprocessing更好用,以后更新知识再补充。
一、python多线程 (有点坑)
鸡肋点
名言:
“Python下多线程是鸡肋,推荐使用多进程!”
那当然有同学会问了,为啥?
背景
1、GIL是什么?
GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定。
2、每个CPU在同一时间只能执行一个线程(在单核CPU下的多线程其实都只是并发,不是并行,并发和并行从宏观上来讲都是同时处理多路请求的概念。但并发和并行又有区别,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。)
在Python多线程下,每个线程的执行方式:
- 获取GIL
- 执行代码直到sleep或者是python虚拟机将其挂起。
- 释放GIL
可见,某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。
在Python2.x里,GIL的释放逻辑是当前线程遇见IO操作或者ticks计数达到100(ticks可以看作是Python自身的一个计数器,专门做用于GIL,每次释放后归零,这个计数可以通过 sys.setcheckinterval 来调整),进行释放。
而每次释放GIL锁,线程进行锁竞争、切换线程,会消耗资源。并且由于GIL锁存在,python里一个进程永远只能同时执行一个线程(拿到GIL的线程才能执行),这就是为什么在多核CPU上,python的多线程效率并不高。
那么是不是python的多线程就完全没用了呢?
在这里我们进行分类讨论:
1、CPU密集型代码(各种循环处理、计数等等),在这种情况下,由于计算工作多,ticks计数很快就会达到阈值,然后触发GIL的释放与再竞争(多个线程来回切换当然是需要消耗资源的),所以python下的多线程对CPU密集型代码并不友好。<