Python38days

发布时间 2023-10-19 20:41:01作者: 拆尼斯、帕丁顿

进程和线程的比较

GIL全局解释器锁(理)

互斥锁

线程队列(线程里使用)

进程池和线程池的用法

协程理论

如何使用协程

基于协程的高并发程序

——————————————————————————————————————————————————————————————————————————————————————————————————

1、进程是操作系统分配任务的基本单位,进程是python中正在运行的程序;当我们打开了1个浏览器时就是开始了一个浏览器进程;
线程是进程中执行任务的基本单元(执行指令集),一个进程中至少有一个线程、当只有一个线程时,称为主线程
2、线程的创建和销毁耗费资源少,进程的创建和销毁耗费资源多;线程很容易创建,进程不容易创建
3、线程的切换速度快,进程慢
4、一个进程中有多个线程时:线程之间可以进行通信;一个进程中有多个子进程时,进程与进程之间不可以相互通信,如果需要通信时,就必须通过一个中间代理实现,Queue、Pipe。
5、多进程可以利用多核cpu,多线程不可以利用多核cpu
6、一个新的线程很容易被创建,一个新的进程创建需要对父进程进行一次克隆
7、多进程的主要目的是充分使用CPU的多核机制,多线程的主要目的是充分利用某一个单核

8.一个进程由一个或者多个线程组成,线程是一个进程中代码的不同执行路线
9.切换进程需要的资源比切换线程的要多的多
10.进程之间相互独立,而同一个进程下的线程共享程序的内存空间(如代码段,数据集,堆栈等)。

某进程内的线程在其他进程不可见。换言之,线程共享同一片内存空间,而进程各有独立的内存空间

————————————————————————————————————————————————————————————————————————————————————————————————

GIL 是python的全局解释器锁,同一进程中假如有多个线程运行,一个线程在运行python程序的时候会霸占python解释器(加了一把锁即GIL),使该进程内的其他线程无法运行,等该线程运行完后其他线程才能运行。如果线程运行过程中遇到耗时操作,则解释器锁解开,使其他线程运行。所以在多线程中,线程的运行仍是有先后顺序的,并不是同时进行

背景信息
1. Python代码运行在解释器上嘛,有解释器来执行或者解释
2. Python解释器的种类:
1、CPython 2、IPython 3、PyPy 4、Jython 5、IronPython
3. 当前市场使用的最多(95%)的解释器就是CPython解释器
4. GIL全局解释器锁是存在于CPython中
5. 结论是同一时刻只有一个线程在执行? 想避免的问题是,出现多个线程抢夺资源的情况

比如:现在起一个线程,来回收垃圾数据,回收a=1这个变量,另外一个线程也要使用这个变量a,当垃圾回收线程还没没有把变量a回收完毕,另一个线程就来抢夺这个变量a使用

如何避免?
如开头介绍的那般,Python这门语言设计之处,就是直接在解释器上添加了一把锁,这把锁就是为了让统一时刻只有一个线程在执行,言外之意就是哪个线程想执行,就必须先拿到这把锁(GIL), 只有等到这个线程把GIL锁释放掉,别的线程才能拿到,然后具备了执行权限

《GIL锁就是保证在统一时刻只有一个线程执行,所有的线程必须拿到GIL锁才有执行权限》

*****************

1. python有GIL锁的原因,同一个进程下多个线程实际上同一时刻,只有一个线程在执行
2. 只有在python上开进程用的多,其他语言一般不开多进程,只开多线程就够了
3. cpython解释器开多线程不能利用多核优势,只有开多进程才能利用多核优势,其他语言不存在这个问题
4. 8核cpu电脑,充分利用起我这个8核,至少起8个线程,8条线程全是计算--->计算机cpu使用率是100%,
5. 如果不存在GIL锁,一个进程下,开启8个线程,它就能够充分利用cpu资源,跑满cpu
6. cpython解释器中好多代码,模块都是基于GIL锁机制写起来的,改不了了---》我们不能有8个核,但我现在只能用1核,----》开启多进程---》每个进程下开启的线程,可以被多个cpu调度执行

 

7. cpython解释器:io密集型使用多线程,计算密集型使用多进程

  

io密集型,遇到io操作会切换cpu,假设你开了8个线程,8个线程都有io操作---》io操作不消耗cpu---》一段时间内看上去,其实8个线程都执行了, 选多线程好一些

 -计算密集型,消耗cpu,如果开了8个线程,第一个线程会一直占着cpu,而不会调度到其他线程执行,其他7个线程根本没执行,所以我们开8个进程,每个进程有一个线程,8个进程下的线程会被8个cpu执行,从而效率高

**************

有了全局解释器锁(GIL)为什么还需要同步锁?(互斥锁是一种最基本的同步锁)

全局解析器锁(GIL)加在了全局了,没有加到我所想要的位置,加到什么位置不是我们决定的;
包括修改资源的程序和非修改资源的程序,如果出现在修改资源的相关代码上,肯定会出现脏数据。
同步锁:来获取一把互斥锁。互斥锁就是对共享数据进行锁定,保证同一时刻只有一个线程操作数据,是数据级别的锁。
GIL锁是解释器级别的锁,保证同一时刻进程中只有一个线程拿到GIL锁,拥有执行权限。

比如:我起了2个线程,来执行a=a+1,a一开始是0
1. 第一个线程来了,拿到a=0,开始执行a=a+1,这个时候结果a就是1了
2. 第一个线程得到的结果1还没有赋值回去给a,这个时候,第二个线程来了,拿到的a是0,继续执行
a=a+1结果还是1
3. 加了互斥锁,就能够解决多线程下操作同一个数据,发生错乱的问题

 

二/////

 执行结果:明显数据混乱了

加同步锁(with方法)

 

加同步锁(acquire方法和release方法)

 ————————————————————————————————————————————————————————————————————————————————————————————————

为什么线程中还有使用队列?

同一个进程下多个线程数据是共享的
为什么先同一个进程下还会去使用队列呢
因为队列是
管道 + 锁
所以用队列还是为了保证数据的安全

线程队列:
1. 先进先出
2. 后进先出
3. 优先级的队列

 

 

————————————————————————————————————————————————————————————————————————————————————————————————

池:池子、容器类型,可以盛放多个元素

进程池:提前定义好一个池子,然后,往这个池子里面添加进程,以后,只需要往这个进程池里面丢任务就行了,然后,有这个进程池里面的任意一个进程来执行任务

线程池:提前定义好一个池子,然后,往这个池子里面添加线程,以后,只需要往这个线程池里面丢任务就行了,然后,有这个线程池里面的任意一个线程来执行任务

进程池和线程池有什么好处呢?

+——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

 

1.协程由于由程序主动控制切换,没有线程切换的开销,所以执行效率极高。对于IO密集型任务非常适用,如果是cpu密集型,推荐多进程+协程的方式。

2.线程的切换会保存到CPU的栈里,协程拥有自己的寄存器上下文和栈,

3.协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈

4.协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态

5. 协程最主要的作用是在单线程的条件下实现并发的效果,但实际上还是串行的(像yield一样)

  协程的作用:是在执行函数A时,可以随时中断,去执行函数B,然后中断继续执行函数A(可以自由切换)。但这一过程并不是函数调用(没有调用语句),这一整个过程看似像多线程,然而协程只有一个线程执行.

进程
资源分配
线程
执行的最小单位
协程
自我认为的产物,它不是在操作系统中实际存在的

 协程就是单线程下的并发
并发:
切换+保存状态
 以前的并发的切换其实是进程或者线程在切换

协程是最节省资源的,进程是最消耗资源的,其次是线程

怎么监测有没有遇到IO?
这里的切换是程序员级别的切换,我们自己切,不是操作系统切的
本质上就是最大限度的利用CP

 

 

 

 

协程的主要特色是:

协程间是协同调度的,这使得并发量数万以上的时候,协程的性能是远远高于线程。

注意这里也是“并发”,不是“并行”。

目的:

想要在单线程下实现并发异步

并发指的是多个任务看起来是同时运行的

并发=切换+保存状态

 

————————————————————————————————————————————————————————————————————————————————————————————————