线程与进程,你真得理解了吗
- python多线程通信:全局变量(需要加锁)、消息队列
- 线程三个基本状态:就绪、运行、阻塞
- 线程五个基本操作:创建、就绪、运行、阻塞、终止
- 进程四种形式:主从式、会话式、消息或邮箱机制、共享存储区方式
- 多线程和多进程使用情况
多线程和多进程 及其应用场景
- 计算机的存储体系中,“三级存储”指的是:高速缓冲存储器(Cache是SRAM)、主存储器(内存是DRAM)、辅助存储器(外存)。高速缓冲存储器用来改善主存储器与中央处理器CPU的速度匹配问题;辅助存储器用于扩大存储空间。另外,还有缓存(buffer)是指在内存中划分出一块区域用于存放常使用的输入输出数据,以缓解CPU与外设处理速度不匹配的问题。
- 内存,Cache都会由于掉电丢失数据,这称为易失性存储器(Volatile Memory),与之对应硬盘时非易失性存储器(Non-volatile Memory)。
- 程序在运行过程中,会将运算需要的数据从主存复制一份到高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。
- 从物理内存上:进程是内存的一片内存空间,在任意时刻cpu只能执行一条指令,执行哪条指令由物理程序由程序计数器决定,也就是说,进程共用一个程序计数器;从逻辑上:由于进程可以执行,也可以被挂起,还能继续执行,因此从这个角度看,每个进程有着自己的计数器;从时间上:进程都是向前推进
- 进程是资源分配的最小单位,线程是CPU调度的最小单位。
- 每一个进程启动时都会最先产生一个线程,即主线程。然后主线程会再创建其他的子线程。
- 与进程相关的资源包括:内存页(同一个进程中的所有线程共享同一个内存空间);文件描述符(e.g. open sockets);安全凭证(e.g.启动该进程的用户ID)
- 并行:同一时间点多个任务同时进行
- 并发:在同一时间段内多个任务同时进行
- 同步 : 多个任务之间执行没有先后顺序,可以同时运行,执行的先后顺序不会有什么影响,存在的多条运行主线
- 异步 : 多个任务执行的时候要求有先后顺序,必须一个先执行完成后,另一个才能继续执行,只有一个主线
- 阻塞 : 从调用者的角度出发,如果在调用的时候,被卡住,不能在继续向下运行,需要等待,就说是阻塞
- 非阻塞 : 从调用者的角度出发,如果在调用的时候,没有被卡住,能够继续向下继续执行,无需等待,就说是非阻塞
- 由于进程之间数据是不共享的,所以不会出现多线程GIL带来的问题。多进程之间的通信通过Queue()或Pipe()来实现
- 单CPU中进程只能是并发,多CPU计算机中进程可以并行。
- 单CPU单核中线程只能并发,单CPU多核中线程可以并行(进程只能并发)。
- 每一个子进程都是由父进程启动
- 双核CPU就是一个CPU里有2个CPU核,就等于汽车有2个发动机1个油箱
- 双CPU一般应用于服务器,就是一个主板上能装2个以上的CPU,2个发动机2个油箱
- 多CPU之间交换数据需要经过以下步骤:1CPU-1内存-主板芯片-2内存-2CPU,交换数据的速度被慢速内存和低速带宽拖累。
- 进程和线程都是为了处理 并发编程 这样的场景,但是进程有问题,频繁创建和释放的时候效率低,相比之下,线程更轻量,创建和释放效率更高。为啥更轻量?少了申请释放资源的过程
- 进程具有独立性,每个进程有各自的虚拟地址空间,一个进程挂了,不会影响到其他进程。同一个进程中的多个线程,共用同一个内存空间,一个线程挂了,可能影响到其他线程的,甚至导致整个进程崩溃
多核与多CPU
MMU(Memory Management Unit,内存管理单元)除了提供地址转换以外,还提供内存保护机制
GIL
CPython 在解释器进程级别有一把锁(线程级别没有锁),叫做GIL(Global Interpreter Lock),即全局解释器锁。
在非python环境中,单核情况下,同时只能有一个任务执行。多核时可以支持多个线程同时执行。但是在python中,无论有多少核,同时只能执行一个线程。究其原因,这就是由于GIL的存在导致的。
GIL来源是为了数据安全所做的决定。某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。GIL只在cpython中才有,因为cpython调用的是c语言的原生线程,所以他不能直接操作cpu,只能利用GIL保证同一时间只能有一个线程拿到数据。
Python中有多个解释器实现办法,分别用C,Java,C#和Python编写的CPython,JPython,IronPython和PyPy是最受欢迎的。GIL只存在于传统的Python实现方法如CPython中。如果你的程序及其库文件可以通过别的实现方式实现,那么你也可以尝试一下。
在CPython中由于有GIL存在,IO密集型,使用多线程较为合算;CPU密集型,使用多进程,要绕开GIL。
Python多线程的工作过程:
- 拿到公共数据
- 申请gil
- python解释器调用os原生线程
- os操作cpu执行运算
- 当该线程执行时间到后,无论运算是否已经执行完,gil都被要求释放
- 进而由其他线程重复上面的过程
- 等其他线程执行完后,又会切换到之前的线程(从他记录的上下文继续执行)
- 整个过程是每个线程执行自己的运算,当执行时间到就进行切换(context switch)
多线程
threading.Thread提供的线程对象方法和属性:
- start():创建线程后通过start启动线程,等待CPU调度,为run函数执行做准备;
- run():线程开始执行的入口函数,函数体中会调用用户编写的target函数,或者执行被重载的run函数;
- join([timeout]):阻塞挂起调用该函数的线程,直到被调用线程执行完成或超时。通常会在主线程中调用该方法,等待其他线程执行完成。
- name、getName()&setName():线程名称相关的操作;
- ident:整数类型的线程标识符,线程开始执行前(调用start之前)为None;
- isAlive()、is_alive():start函数执行之后到run函数执行完之前都为True;
- daemon、isDaemon()&setDaemon():守护线程相关;
多进程
- 针对GIL缺点,最流行的方法是应用多进程方法,在这个方法中你使用多个进程而不是多个线程。每一个Python进程都有自己的Python解释器和内存空间,因此GIL不会成为问题。Python拥有一个multiprocessing模块可以帮助我们轻松创建多进程。
- 顺便提一句,python提供了Sunprocess模块可以在程序执行过程中,调用外部的程序。
1、同步函数:当一个函数是同步执行时,那么当该函数被调用时不会立即返回,直到该函数所要做的事情全都做完了才返回。
2、异步函数:如果一个异步函数被调用时,该函数会立即返回尽管该函数规定的操作任务还没有完成。
3、同步函数subprocess.run()、异步函数subprocess.Popen()
搞定python多线程和多进程
python多线程与多进程及其区别
Python进阶(十)多进程multiprocessing和subprocess模块
一文看懂Python多进程与多线程编程(工作学习面试必读)
计算机是如何工作的,Java多线程编程
Python中的全局解释器锁