进程
1. 进程的概念
一个正在运行的程序或者软件就是一个进程,它是操作系统进行资源分配的基本单位,也就是说每启动一个进程,操作系统都会给其分配一定的运行资源(内存资源)保证进程的运行。
比如:现实生活中的公司可以理解成是一个进程,公司提供办公资源(电脑、办公桌椅等),真正干活的是员工,员工可以理解成线程。
注意:
一个程序运行后至少有一个进程,一个进程默认有一个线程,进程里面可以创建多个线程,线程是依附在进程里面的,没有进程就没有线程。
多进程的使用
- 导入进程包
- import multiprocessing
- 创建子进程并指定执行的任务
- sub_process = multiprocessing.Process (target=任务名)
- 启动进程执行任务
- sub_process.start()
进程的注意点
- 进程之间不共享全局变量
- 创建子进程会对主进程资源进行拷贝,也就是说子进程是主进程的一个副本,好比是一对双胞胎,之所以进程之间不共享全局变量,是因为操作的不是同一个进程里面的全局变量,只不过不同进程里面的全局变量名字相同而已。
- 主进程会等待所有的子进程执行结束再结束
线程
线程的概念
线程是进程中执行代码的一个分支,每个执行分支(线程)要想工作执行代码需要cpu进行调度 ,也就是说线程是cpu调度的基本单位,每个进程至少都有一个线程,而这个线程就是我们通常说的主线程。
- 线程是Python程序中实现多任务的另外一种方式,线程的执行需要cpu调度来完成。
多线程的使用
- 导入线程模块
- import threading
- 创建子线程并指定执行的任务
- sub_thread = threading.Thread(target=任务名)
- 启动线程执行任务
- sub_thread.start(
线程的注意点
- 线程之间执行是无序的
- 主线程会等待所有的子线程执行结束再结束
- 线程之间共享全局变量
进程和线程的对比
关系对比
- 线程是依附在进程里面的,没有进程就没有线程。
- 一个进程默认提供一条线程,进程可以创建多个线程。
区别对比
-
进程之间不共享全局变量
-
线程之间共享全局变量,但是要注意资源竞争的问题,解决办法: 互斥锁或者线程同步
-
创建进程的资源开销要比创建线程的资源开销要大
-
进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
-
线程不能够独立执行,必须依存在进程中
-
多进程开发比单进程多线程开发稳定性要强
协程
协程又称微线程,纤程。
它是比线程更小的执行单元,因为它自带CPU上下文。这样只要在合适的时机,我们可以把一个协程切换到另一个协程当中。
只要这个过程保存或恢复CPU上下文,那么程序就可以运行。
通俗的理解:在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,并且切换的次数以及什么时候再切换到原来的函数由开发者确定。
协程和线程有什么不同
那么这个过程看起来和线程差不多。其实不然, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。
操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作。
所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上万次系统都抗的住。
协程的实现
协程帮助你记住哪个任务执行到哪个位置上了,并且实现安全的切换
一个任务一旦阻塞卡顿,立刻切换到另一个任务继续执行,保证线程总是忙碌的,更加充分的利用CPU,抢占更多的时间片。
1)greenlet:一个第三方模块,需要提前安装 pip3 install greenlet才能使用;
2)yield生成器:借助生成器的特点亦可以实现协程代码;
3)asyncio:在python3.4 种引入的模块,用于编写协程代码;
说明:主要通过装饰器 @asyncio.coroutine 来实现协程函数定义;Python3.8之后 @asyncio.coroutine 装饰器会被移除,推荐使用async & awit 关键字实现协程代码。
4)async & awiat:在python3.5中引入的两个关键字,结合asyncio模块使用;
线程的缺陷
python中的线程可以并发,但是不能并行(同一个进程下的多个线程不能分开被多个cpu同时执行)
原因:
全局解释器锁(Cpython解释器特有) GIL锁:
同一时间,一个进程下的多个线程只能有一个被cpu执行,不能实现线程的并行操作
想要并行的解决办法:
(1)用多进程间接实现线程的并行,并不理想,开辟空间消耗资源
(2)换一个Pypy,Jpython解释器 也不好用,兼容性问题
1、GIL锁不是python的特点。而是cpython的特点。
2、在cpython解释器中,GIL是一把互斥锁,用来保证进程中同一个时刻只有一个线程在执行。
3、总结
程序分为计算密集型和io密集型
对于计算密集型程序会过度依赖cpu,但网页,爬虫,OA办公,这种io密集型的程序里,python多线程绰绰有余
对于IO密集型应用,即便有GIL存在,由于IO操作会导致GIL释放,其他线程能够获得执行权限。由于多线程的通讯成本低于多进程,因此偏向使用多线程。
对于计算密集型应用,由于CPU一直处于被占用状态,GIL锁直到规定时间才会释放,然后才会切换状态,导致多线程处于绝对的劣势,此时可以采用多进程+协程。