总叙
进程是 内存资源分配的单位
线程是 操作系统调度的单位,真正执行任务的是线程
协程是线程中 任务细分下来的单位
代码是由线程执行的
线程必须在进程中运行
线程可以拆分成多个协程
从图中可以看出
一个进程中有1或多个线程,进程就像一个容器一样,是内存资源分配的单位
线程是由操作系统调度的,把CPU计算资源给哪个线程使用是操作系统管的
每个线程里或多或少有几个协程,这些是程序管理的
Note
程序 > 进程 > 线程 > 协程
- 代码写好了,躺在硬盘了,那叫 程序
- 代码拿到内存里跑,那叫 进程
- 程序觉得一个主进程不够,可以再开N个 子进程
- 进程只是一个容器,需要干活的,所以得有 线程
- 进程觉得一个主线程不够,可以再开N个 子线程
- 线程觉得有很多耗时操作(如读写文件、网络请求),想把等待的时间利用起来做其他还没完成的任务,可以搞出很多个 协程
这么多个概念,其被发明的目的都是为了 更高效的利用计算机
在上古时代,单CPU-单核,在同一时间内只能做一件事情,不可能像现在这样又听歌又上网又写文档 人们发现CPU的计算速度奇高,所以利用了时间片(就是短到人类无法发现的时长)多次切换运行程序,雨露均沾 这样 单CPU-单核 也能实现多个任务同时进行。 不过这只是看起来像是在同时运行,如果你能暂停时间,你会发现CPU只是在执行某一个程序而已。 这种让CPU资源在程序之间反复横跳,从而实现多任务的技术,叫做 并发
那如果我就是想要按下时间暂停按钮的时候,真的有多个程序在一起执行怎么办? 再加个CPU咯。这叫 并行
举个栗子¶
现在有一个玩具厂,厂里面有2个车间:一个生产玩具,一个生产家电 每个车间有N条流水线,每条流水线上有N个工人,每个工人都有各自的任务 车间需要地方搭建,还需要电力供应
我们捋一捋关系:
发电机 -> CPU 厂 -> 内存 车间 -> 进程 流水线 -> 线程 工人 -> 任务
车间没建起来,就像代码放在硬盘里,代码扔硬盘里不就叫 程序 么
把 车间 建到 厂房 里用起来,那些 车间 就成了 有用的车间 把 代码 放到 内存 里跑起来,那些 代码 就成了 进程
车间 搭好了得有 流水线 来 组装产品啊 进程 建好了还得有 线程 来 执行任务啊
所以每个车间至少有一条主流水线 所以每个进程至少有一个主线程
车间 一开始只有一条主流水线做全部步骤,有天发现有个步骤特别麻烦 进程 一开始只有一个 主线程 做所有事情,有天发现有个操作特别耗时
于是主流水线就再拉了一条副流水线来负责这个麻烦的步骤,自己继续下一步,再有麻烦步骤就继续加副流水线 于是主线程开启一个 子线程 来负责这个耗时操作,主线程继续执行,再有耗时操作就继续加子线程
有一天单子太多,一条车间不够,那就加条副车间咯,再不够再加 有一天计算量太大,一个进程不够,那就加个 子进程 咯,再不够再加
而且副车间得更主车间一模一样 而且子进程得完全复制主进程,包括代码、状态等等
并且,一个车间挂了没事,不会影响其他车间 并且,一个进程挂了没事,不会影响其他进程
那现在车间之间 要传递零件怎么办,搭个管子,直接溜过去,先放的别人就先拿到 那现在 进程之间 要进行 通信 怎么办,用 队列 或者 管道 呗,先放的就先拿到
那流水线之间要传递东西怎么办?放车间的工具车上不就完了 那线程之间要 通信 怎么办?用 全局变量 不就完了
流水线跟车间可不一样,要是哪条副流水线挂了是会影响整个车间的 线程跟进程可不一样,要是哪个子线程挂了是会影响整个进程的
主流水线要是挂了,整个车间完犊子 主线程要是挂了,整个进程完犊子
有的流水线就一个工人,有的流水线有多个工人,这个工人动作慢了,就把螺丝刀给别的工人先用 有的线程就一个协程,有的线程有多个协程,这个协程阻塞了,就把运行权给别的协程
这种积极利用等待时间安排其他工人的模式,叫做高效利用时间 这种积极利用等待时间做其他任务的形式,叫做 协程
那现在发电机只有一个,生产玩具的车间要用,生产家电的车间要用,老板又想两个车间同时生产 那现在CPU只有一个,进程A要用,进程B要用,用户又想两个进程使用运行
那就让发电机快速在两个车间之间快速切换,哪个车间有电了立马干活,没电了立马暂停 那就让CPU资源在两个进程之间反复横跳,哪个进程被选中了立马运行,时间片到了立马暂停
因为速度非常快,所以老板看着以为两个车间都启动了 因为速度非常快,所以用户真的以为两个任务同时运行,这叫 并发
有一天老板发财,加了一台发电机,这下两个车间都有电了,真正的两个车间同时生产 有一天用户发财,加了一个CPU,这下两个进程都有资源了,真正实现两个进程同时运行,这叫 并行
进程占的资源比较大,但是安全,一个进程挂掉不会导致另外一个进程挂掉 线程切换开销更小 协程利用等待的时间做别的事,但是需要程序承担调度责任
在不考虑 GIL(全局锁)的情况下,优先用协程、再用线程、最后用进程