-- 多任务-多线程-多进程-协程-进阶学习 --
文中所提到的案例参考:GITHUB中项目文件夹
https://github.com/FangbaiZhang/Python_advanced_learning/tree/master/02_Python_advanced_grammar_supplement/002_%E5%A4%9A%E7%BA%BF%E7%A8%8B_%E5%A4%9A%E8%BF%9B%E7%A8%8B_%E5%8D%8F%E7%A8%8B_%E8%BF%9B%E9%98%B6%E6%B1%87%E6%80%BB
1 多任务
1.1 多任务概念
-
多任务举例
什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务。
打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word赶作业,这就是多任务,
至少同时有3个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。 -
电脑多核单核
多核CPU已经非常普及了,但是,即使过去的单核CPU,也可以执行多任务。
由于CPU执行代码都是顺序执行的,那么,单核CPU是怎么执行多任务的呢?答案就是操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,
任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。
表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,
我们感觉就像所有任务都在同时执行一样。真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,
所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。 -
并发与并行概念
并发:指的是任务数大余cpu核数,通过操作系统的各种任务调度算法,
实现用多个任务“一起”执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)
并行:指的是任务数小于等于cpu核数,每个cup核同时执行不同的任务,即所有的任务真的是一起执行的(物理意义上的多任务)- 当多进程的任务数小于CPU的核数,多个任务同时执行,可以实现物理意义上的并行
- 但实际中,多进程任务数远远大于CPU核数,就只能通过调度轮流执行,实现多并发
-
关于并发、并行区别与联系
如果你的代码是IO密集型的,线程和多进程可以帮到你。多进程比线程更易用,但是消耗更多的内存。
如果你的代码是CPU密集型的,多进程就明显是更好的选择——特别是所使用的机器是多核或多CPU的。
对于网络应用,在你需要扩展到多台机器上执行任务,RQ是更好的选择。并发是指,程序在运行的过程中存在多于一个的执行上下文。这些执行上下文一般对应着不同的调用栈。
在单处理器上,并发程序虽然有多个上下文运行环境,但某一个时刻只有一个任务在运行。但在多处理器上,因为有了多个执行单元,就可以同时有数个任务在跑。
这种物理上同一时刻有多个任务同时运行的方式就是并行。
和并发相比,并行更加强调多个任务同时在运行。
而且并行还有一个层次问题,比如是指令间的并行还是任务间的并行。 -
多进程和多线程区别:
-
多进程:多个代码片段在独立同时进行,可以同时利用多个内核,真正的多并发,同一时刻占用内存为多个进程之和
-
多线程:多个代码片段同时执行,由于存在GIL,本质上,同时只有一个线程再执行,只有一个进程占用内存
这个不执行,下一个才执行,内部先获取锁,释放锁,并不能发挥多核性能 -
因此,多线程是伪多并发,同一时刻占用内存还是一个进程的
-
Python使用多进程才能利用CPU的多核资源
-
当多进程的任务数小于CPU的核数,多个任务同时执行,可以实现物理意义上的并行
-
但实际中,多进程任务数远远大于CPU核数,就只能通过调度轮流执行,实现多并发
-
-
全局解释器(GIL)
-
Python代码的执行是由python虚拟机进行控制
-
Python语言本身并没有GIL问题,是python虚拟机(Python解释器)的问题
-
在主循环中只能有一个线程在执行,每个线程执行的时候都需要先获取GIL,
-
以保证同一时刻只有一个线程在执行代码
-
线程释放GIL锁的情况:
-
在IO操作等可能会引起阻塞的system call之前,可以暂时释放GIL,
-
但在执行完毕后,必须重新获取GIL
-
解决多线程的GIL问题:
-
方法1:更换python解释器(默认解释器是C语言编写的),换成java写的解释器jypython
-
方法2:用其它语言替代多线程代码,python胶水语言,可以直接调用c语言,java语言等写的代码
-
-
多线程和多进程的使用
- 计算密集型(多进程):代码没有等待时间,使用多进程,发挥多核的性能
- IO密集型(多线程,协程):(input,output),比如网络收发,文件读写,具有等待时间,优先使用协程,再考虑多线程
- 比如:请求网络,发出请求,假设需要等待时间1分钟,然后返回内容,
- 这个等待时间就可以先解锁当前线程,因为还在等待返回内容,开始下个线程
- 等待的1分钟内可以发出多个请求,返回内容回来时候,再次请求锁,不断上锁解锁
- 网页爬取:多线程比单线程爬取性能有提升,因为遇到IOS阻塞会自动释放GIL锁
2. 线程
-
多线程是伪多并发
多个代码片段同时执行,由于存在GIL,本质上,同时只有一个线程再执行,
这个不执行,下一个才执行,内部先获取锁,释放锁
因此,多线程是伪多并发,占用内存还是一个进程的 -
线程
- 一个进程的独立运行片段,一个进程可以有多个线程
- 轻量化的进程
- 一个进程的多个线程共享数据和上下文运行环境
- 多线程共享全局变量
- 共享互斥问题
-
线程常用属性
-
threading.currentThread: 返回当前的线程变量
-
threading.enumerate(): 返回一个包含正在运行所有线程的list。
-
正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
-
threading.activeCount(): 返回正在运行的线程数量,
-
与len(threading.enumerate())有相同的结果。
-
除了使用方法外,线程模块
-